Syntax/Semantics Exceptions In Go

This article will list the syntax/semantics sugars and exceptions in Go. Sugars are special syntax/semantics exceptions to make programming convenient.

Nested Function Calls

The basic rule:
If the return results of a function call exactly match the input parameters of another function call and the number of the return results is not zero, then the first call can be nested in the second one. When nested in the second call, the first call can't mix up with other parameters of the second call.
Sugar:
If the number of the return results of a function call is exactly one, then the function call can be nested in another function call, as long as the other function call need at least one argument, and the single-return function call can mix up with other arguments of the other function call.
Exception:
The basic rule doesn't apply to calls of some built-in functions with multiple input parameters, including copy, delete, print and println functions. A calls to any of these functions can't take a solo multi-return function call as the only argument.

(The basic rule may apply to copy functions later, but will never apply to print and println functions.)

The basic rule applies to the calls of built-in append and complex functions, with one exception scenario (for the standard Go compiler):
Calls to the complex function can't take a solo method call on an interface value receiver as the only argument.

(This exception scenario may be a bug of the standard Go compiler. At present, Go 1.10, the bug still exists.)

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) {}

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)
	println(f1())
	_ = complex(I(T{}).m())
	*/
}

Unused Variables

The basic rule:
Local variables can't be declared but not used.
Exception:
Function parameter and result variables, which can also be viewed as local variables, are allowed to be not used.

Access Struct Fields

The basic rule:
Pointer values have no fields.
Sugar:
Pointers of a struct value can access the fields of the struct value.
Example:
package main

type T struct {
	x int
}

func main() {
	var t T
	var p = &t
	
	p.x *= 2
	// above line is a sugar for
	(*p).x *= 2
}

Method Receivers

The basic rule:
The methods explicitly defined on type *T are not methods of type T.
Sugar:
Although the methods explicitly defined on type *T are not methods of type T, addressable values of type T can call these methods.
Example:
package main

type T struct {
	x int
}

func (pt *T) Double() {
	pt.x *= 2
}

func main() {
	var t = T{3}
	
	t.Double() // t.x == 6 now
	// above line is a sugar for
	(&t).Double()
}

Take Addresses Of Composite Literal Values

The basic rule:
Literal values are not addressable and can't be taken addresses.
Sugar:
Although composite literal values are not addressable, they can be taken addresses explicitly.
Example:
package main

type T struct {
	x int
}

func (pt *T) Double() {
	pt.x *= 2
}

func main() {
	// T{3}.Double() // error: T{3} is not addressable
	                 // The last sugar doesn't work here.
	
	var pt = &T{3} // sugar: explicitly take address of
	               // unaddressable composite literal.
	pt.Double()
}

Selectors On Defined One-Level Pointers

The basic rule:
Generally, selectors can be used on values of defined pointer types.
Sugar:
If x is a value of a defined one-level pointer type, and selector (*x).f is a valid selector, the x.f is a legal shorthand (sugar) for (*x).f.

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 // valid
	_ = p.x    // legal
	
	_ = (*p).y // valid
	// _ = p.y // illegal
	
	// Following ones are all illegal.
	/*
	_ = ppt.x
	_ = ppt.y
	_ = pp.x
	_ = pp.y
	*/
}

Precedences Of Unary Operators

The basic rule:
For any operand, its post operator has a higher precedence than its pre operator if the post operator is not a binary operator.
Sugar:
Assume operand ptr is a pointer value, in *ptr++, operator * has a higher precedence than operator ++, a.k.a., *ptr++ is equivalent to (*ptr)++, for pointer arithmetic is not supported in Go.
Example:
package main

import (
	"fmt"
)

type T struct{}

func (t T) M() *int {
	var v = 123
	return &v
}

func main() {
	var t = &T{}
	var a = *t.M()
	var b = *(t.M())
	// The above two lines are equivalent.
	var c = (*t).M()
	fmt.Printf("%T %T %T\n", a, b, c) // int int *int
	
	var s = [...]int{0, 1, 2}
	var u = &s[0]
	var v = &(s[0])
	// The above two lines are equivalent.
	var w = (&s)[0]
	fmt.Printf("%T %T %T\n", u, v, w) // *int *int int
	
	// sugar
	
	var x = new(int)
	*x++ // *x == 1 now
	// The Above line is a sugar for
	(*x)++
}

The Addressability Of Container Elements

The basic rule:
Container elements are addressable if their values can be overwritten.
Exception:
Map elements are always not addressable, even if they can be overwritten.
Sugar:
Elements of a slice are always addressable, even if the slice itself is not addressable.
Example:
package main

func main() {
	var s = []int{123, 789}
	var p = &s[0] // ok
	_ = p
	
	var m = map[string]int{"abc": 123}
	_ = m
	
	// The exception:
	// p = &m["abc"] // error: cannot take addresses of map elements
	
	// The sugar:
	f := func() []int { // return results are not addressable
		return []int{0, 1, 2}
	}
	_ = &f()[2] // ok
}

Modify Unaddressable Values

The basic rule:
Unaddressable values can't be modified. In other words, unaddressable values should 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}}
	mt["abc"] = T{x: 789} // replacement is ok.
	// Partial modification is not allowed.
	/*
	mt["abc"].x = 456
	*/
}

Generic

The basic rule:
Go doesn't support generic.
Exception:
Most built-in functions, which declared in the builtin and unsafe standard packages, support generic. And type compositions with all kinds of composite types in Go is kinda of generic.

Function Names In One Package

The basic rule:
Names of declared functions can't be duplicated in one pacakge.
Exception:
There can be multiple functions declared with names as init.

Function Calls

The basic rule:
Functions can be called by user code.
Exception:
init functions can't be called by user code.

Functions Being Used As Values

The basic rule:
Declard functions can be used as function values.
Exception 1:
None of the built-in functions, which declared in the builtin and unsafe standard packages, can be used as function values, including the non-generic panic and recover functions.
Exception 2:
init functions can not be used as function values.
Example:
package main

import "fmt"
import "unsafe"

func main() {
	// These ones are okay.
	var _ = main
	var _ = fmt.Println
	
	// These ones fail to compile.
	var _ = panic
	var _ = unsafe.Sizeof
}

Absence Of Return Values Of Function Calls

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

Declared Varialbes

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

Argument Consistency Of The Built-in copy and append Functions

The basic rule:
The type of the second slice argument of a copy or append function call must be consistent with the first slice argument. (For append call, assume the second argument is passed with the form aSlice....)
Sugar:
If the type of the first slice argument of a copy and append function call is []byte, then the second argument type can be string. (For append call, assume the second argument is passed with the form aSlice....)
Example:
package main

func main() {
	var bs = []byte{1, 2, 3}
	var s = "xyz"
	
	copy(bs, s)
	// above line is a sugar (and an optimization) for
	copy(bs, []byte(s))
	
	bs = append(bs, s...)
	// above line is a sugar (and an optimization) for
	bs = append(bs, []byte(s)...)
}

Comparison

The basic rule:
Most types in Go support comparison.
Exception:
Map, slice and function types don't support comparison.
Exception in exception:
Map, slice and function values can be compared to the 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: function values can't be compared
	_ = f1 == nil
	_ = f2 == nil
}

Comparing Interface Values

The basic rule:
Comparing interface values with uncomparable dynamic values will panic.
Exception:
If the dynamic types of two interface values are not the same one, then comparing them will never panic, even if either of the two dynamic values are uncomparable.
Example:
package main

func main() {
	var s = []int{1, 2, 3}
	var m = map[string]int{}
	var x interface{} = s
	var y interface{} = m
	var z interface{} = nil
	// The dynamic values of x and y are both uncomparable.
	//_ = x == x // will panic
	//_ = y == y // will panic
	_ = x == z // false, will not panic
	_ = x == y   // false, will not panic
}

The Blank Composite Literals

The basic rule:
If a type T support composite literal values, 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) will return 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
}

Container Element Iterations

The basic rule:
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.
Exception 3:
Array pointers can also be ranged to iterate array elements.
BTW, accessing array elements on array pointers (pArray[index]) and deriving slices from array pointers (pArray[start : end]) are also legal.

Built-in Types

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

Exported Identifiers

The basic rule:
The identifiers not starting with Unicode upper case letters can't be exported.
Exception:
Identifiers starting with Unicode lower case letter in the builtin package can be exported.

Types Of Values

The basic rule:
Values should have either a type or a default type.
Exception:
Untyped nil has neither a type nor a default type.
For the reason of this exception, untyped nil can't be assigned to a new declared variable which type is not specified. And although untyped nil can be used as dynamic values of interface values, it doesn't implement any interface type.
package main

import "fmt"

func main() {
	const x = 123 // for untyped constant x, its default type is int.
	
	fmt.Printf("type of %v is %T \n", x, x) // type of 123 is int
	
	var y = x // compile okay, the type of y is int
	_ = y
	var _ float64 = x    // compile okay
	var _ int32 = x      // compile okay
	var _ complex128 = x // compile okay
	
	// var _ = nil // compile error: use of untyped nil
	
	var v interface{}
	fmt.Printf("type of %v is %T \n", v, v) // type of <nil> is <nil>
	var _ interface{} = v.(interface{}) // will panic
}

Constant Values

The basic rule:
Constant values never change. Constant can be assigned to variables.
Exception:
iota is a built-in constant predeclared as 0, but its value is not constant. Its value will start from 0 and increase one line by line in a constant declaration group, though the increasements happen at compile time. And iota can only be used in constant declarations. It can't be used in variable declarations and many other places where general constants can show up.

Behaviors Of Missing The OK Optional Return Values

The basic rule:
Whether or not the ok return value is present will not affect program behavior.
Exception:
Missing the ok optional return value will make current goroutine panic if a 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 <- 123
	close(c)
	_, ok = <-c // will not panic
	_ = <-c     // will not panic
	
	var v interface{} = "abc"
	_, ok = v.(int) // will not panic
	_ = v.(int)     // will panic!
}
Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies description flaws and code bugs.