Gold, an experimental Go local docs server, Go docs generation tool, and code reader. NEW!

-- show type implemention relations --

-- show code statistics --

-- smooth code view experiences --

-- and more... --

Syntax/Semantics Exceptions in Go

This article will list all kinds of syntax/semantics exceptions in Go. Some of these exceptions are syntactic sugars to make programming convenient, some are caused built-in generic privileges, some exists for history reasons, and some exists for other reasons in logic.

Nested function calls

If the number of the return results of a function call is not zero, and the return results can be used as the whole arguments of another function call, then the former function call can be nested in the latter function call, the former nested call can't mix up with other arguments of the latter nesting call. Sugar: If a function call returns exactly one result, then the function call can be always be used as a single-value argument in other function calls, the single-result function call can mix up with other arguments of the nesting function calls. Exception (before Go Toolchain 1.15): For the standard Go compiler (but not for gccgo), the basic rule doesn't apply to nesting calls to built-in print and println functions. Calls to these functions can't nest multi-result function calls as arguments. Example: package main import ( "fmt" ) func f0() float64 {return 1} func f1() (float64, float64) {return 1, 2} func f2(float64, float64) {} func f3(float64, float64, float64) {} func f4()(x, y []int) {return} func f5()(x map[int]int, y int) {return} type I interface {m()(float64, float64)} type T struct{} func (T) m()(float64, float64) {return 1, 2} func main() { // These lines compile okay. f2(f0(), 123) f2(f1()) fmt.Println(f1()) _ = complex(f1()) _ = complex(T{}.m()) f2(I(T{}).m()) // These lines don't compile. /* f3(123, f1()) f3(f1(), 123) */ // This line compiles okay only since // Go Toolchain 1.15. println(f1()) // The following 3 lines compile okay // only since Go Toolchain 1.13. copy(f4()) delete(f5()) _ = complex(I(T{}).m()) } The basic rule:Sugar:Exception (before Go Toolchain 1.15):Example:

Select struct fields

Pointer values have no fields. Sugar: We can select the fields of a struct value through pointers of the struct value. Example: package main type T struct { x int } func main() { var t T var p = &t p.x *= 2 // The above line is a sugar of the following line. (*p).x *= 2 } The basic rule:Sugar:Example:

Receiver arguments of method calls

The methods explicitly declared for type *T are not methods of type T for sure. Sugar: Although the methods explicitly defined on type *T are not methods of type T , addressable values of type T can be used as the receiver arguments of calls to these methods. Example: package main type T struct { x int } func (pt *T) Double() { pt.x *= 2 } func main() { // T{3}.Double() // This line fails to compile. var t = T{3} t.Double() // t.x == 6 now // The above line is a sugar of the following line. (&t).Double() // t.x == 12 now } The basic rule:Sugar:Example:

Take addresses of composite literal values

Literal values are unaddressable and unaddressable values can't be taken addresses. Sugar: Although composite literal values are not addressable, they can be taken addresses explicitly. The basic rule:Sugar: Please read structs and containers for details.

Selectors on defined one-Level pointers

Generally, selectors can't be used on values of defined pointer types. If x is a value of a defined one-level pointer type, and selector (*x).f is a legal selector, then the x.f is also a legal selector, it can be viewed as a shorthand of (*x).f . The basic rule:Sugar: Selectors can never be used on values of multi-level pointer types, no matter whether the multi-level pointer types are defined or not. Exception of the sugar: The sugar is only valid if f denotes a struct field, it is not valid if f denotes a method. Example: package main type T struct { x int } func (T) y() { } type P *T type PP **T // a multi-level pointer type func main() { var t T var p P = &t var pt = &t // type of pt is *T var ppt = &pt // type of ppt is **T var pp PP = ppt _ = pp _ = (*p).x // legal _ = p.x // also legal (for x is a field) _ = (*p).y // legal // _ = p.y // illegal (for y is a method) // Following ones are all illegal. /* _ = ppt.x _ = ppt.y _ = pp.x _ = pp.y */ } Exception of the sugar:Example:

The addressability of a container and its elements

If a container is addressable, then its elements are also addressable. Exception: Elements of a map are always unaddressable, even if the map itself is addressable. Sugar: Elements of a slice are always addressable, even if the slice itself is not addressable. Example: package main func main() { var m = map[string]int{"abc": 123} _ = &m // okay // The exception: // p = &m["abc"] // error: map elements are unaddesable // The sugar: f := func() []int { // return results are unaddressable return []int{0, 1, 2} } // _ = &f() // error: f() is unaddressable _ = &f()[2] // okay } The basic rule:Exception:Sugar:Example:

Modify unaddressable values

Unaddressable values can't be modified. In other words, unaddressable values shouldn't appear in assignments as destination values. Exception: Although map element values are unaddressable, they can be modified and appear in assignments as destination values. (But map elements can't be modified partially, they can only be overwritten wholly, a.k.a., replaced.) Example: package main func main() { type T struct { x int } var mt = map[string]T{"abc": {123}} // Map elements are unaddressable. // _ = &mt["abc"] // error // Partial modification is not allowed. // mt["abc"].x = 456 // error // It is ok to replace a map element as a whole. mt["abc"] = T{x: 789} } The basic rule:Exception:Example:

Function Parameters

Each parameter is a value of some type. Exception: The first parameters of the built-in make and new functions are types. The basic rule:Exception:

Function names in one package

Names of declared functions can't be duplicated in one package. Exception: There can be multiple functions declared with names as init (and types as func() ). The basic rule:Exception:

Function calls

Functions whose names are not the blank identifier can be called in user code. Exception: init functions can't be called in user code. The basic rule:Exception:

Functions being used as values

Declared functions can be used as function values. Exception 1: None of the built-in functions, which are documented in the builtin and unsafe standard packages, can be used as function values. Exception 2: init functions can not be used as function values. Example: package main import "fmt" import "unsafe" func init() {} func main() { // These ones are okay. var _ = main var _ = fmt.Println // These ones fail to compile. var _ = panic var _ = unsafe.Sizeof var _ = init } The basic rule:Exception 1:Exception 2:Example:

Discard return values of function calls

The return values of a function call can be discarded alltogether. Exception: The return values of calls to the built-in functions which are documented in the builtin and unsafe standard packages, can't be discarded, if the called function has return results. Exception in exception: The return values of a call to the built-in copy and recover functions can be all discarded, even if the two functions have return results. The basic rule:Exception:Exception in exception:

Declared variables

Declared variables are always addressable. Exception: The predeclared nil variable is not addressable. nil is an immutable variable. The basic rule:Exception:So,is an immutable variable.

Argument passing

An argument can be passed to the corresponding function parameter only if the argument is assignable to the corresponding function parameter type. Sugar: If the first slice argument of a copy and append function call is a byte slice, then the second argument can be a string, whereas a string value is not assignable to the second parameter type (also a byte slice). (For an append call, assume the second argument is passed with the form arg... .) Example: package main func main() { var bs = []byte{1, 2, 3} var s = "xyz" copy(bs, s) // The above line is a sugar (and an optimization) // for the following line. copy(bs, []byte(s)) bs = append(bs, s...) // The above line is a sugar (and an optimization) // for the following line. bs = append(bs, []byte(s)...) } The basic rule:Sugar:Example:

Comparisons

Map, slice and function types don't support comparison. Exception: Map, slice and function values can be compared to the predeclared untyped nil identifier. Example: package main func main() { var s1 = []int{1, 2, 3} var s2 = []int{7, 8, 9} //_ = s1 == s2 // error: slice values can't be compared _ = s1 == nil // ok _ = s2 == nil // ok var m1 = map[string]int{} var m2 = m1 // _ = m1 == m2 // error: map values can't be compared _ = m1 == nil _ = m2 == nil var f1 = func(){} var f2 = f1 // _ = f1 == f2 // error: functions can't be compared _ = f1 == nil _ = f2 == nil } The basic rule:Exception:Example:

Comparisons 2

If a value is assignable to the type of another value, then the two values can be compared. Exception: The values of a non-interface incomparable type can't be compared to values of an interface type, even if the non-interface incomparable type implements the interface type (so values of the non-interface incomparable type are assignable to the interface type). Please read The basic rule:Exception:Please read comparison rules for examples.

Blank composite literals

If the values of a type T can be represented with composite literals, then T{} is its zero value. Exception: For a map or a slice type T , T{} isn't its zero value. Its zero value is represented with nil . Example: package main import "fmt" func main() { // new(T) returns the address of a zero value of type T. type T0 struct { x int } fmt.Println( T0{} == *new(T0) ) // true type T1 [5]int fmt.Println( T1{} == *new(T1) ) // true type T2 []int fmt.Println( T2{} == nil ) // false type T3 map[int]int fmt.Println( T3{} == nil ) // false } The basic rule:Exception:Example:

Container element iterations

Only container values can be ranged, the iterated values are container elements. The element key/index will also be returned alongside of each iterated element. Exception 1: The iterated values are runes if the ranged containers are strings, instead of the byte elements of strings. Exception 2: The element index (order) will not be returned alongside of each iterated element when iterating channels. Sugar: Array pointers can also be ranged to iterate array elements, though pointers are not containers. The basic rule:Exception 1:Exception 2:Sugar:

Methods of built-in types

Generally, built-in types have no methods. Exception: The built-in error type has a Error() string method. The basic rule:Exception:

Types of values

Each value has either a type or a default type. Exception: Untyped nil has neither a type nor a default type. The basic rule:Exception:

Constant values

Constant values never change. Constant can be assigned to variables. Exception: Predeclared iota is a built-in constant which is bound with 0 , but its value is not constant. Its value will start from 0 and increase one constant specification by constant specification in a constant declaration, though the increments happen at compile time. Exception 2: iota can only be used within constant declarations. It can't be assigned to variables in variable declarations. The basic rule:Exception:Exception 2:

Behavior change caused by discarding the optional evaluation results of expressions

Whether or not the optional evaluation result of an expression presents will not affect program behavior. Exception: Missing the optional result value in a type assertion will make current goroutine panic if the type assertion fails. Example: package main func main() { var ok bool var m = map[int]int{} _, ok = m[123] // will not panic _ = m[123] // will not panic var c = make(chan int, 2) c The basic rule:Exception:Example: