Go Details 101

This article will list all kinds of details in Go. Some of these details are shown in other Go 101 articles, some are shown in the following sections.

A package can be imported more than once in a source file

A Go source file can imports the same package multiple times, but the import names must be different. These same-pacakge imports reference the same package instance.

For example:
package main

import "fmt"
import "io"
import inout "io"

func main() {
	fmt.Println(&inout.EOF == &io.EOF) // true
}

Elements of unaddressable arrays are also unaddressable, but elements of unaddressable slices are always addressable

The reason is the elements of an array value and the array will be stored in the same memory block when the array is stored in memory. But the situation is different for slices.

An example:
package main

func main() {
	// Container composite literals and map elements are all unaddressable.

	// Take container element addresses.
	_ = &[]int{1}[0] // ok
	_ = &[5]int{}[0] // error: cannot take the address of [5]int literal[0]
	_ = &(&[5]int{})[0] // ok
	_ = &(*&[5]int{})[0] // ok

	// Modify container element values.
	map[int]int{}[1] = 9
	[]int{1,2,3}[1] = 9
	[3]int{1,2,3}[1] = 9 // error: cannot assign to [3]int literal[1]
	(&[3]int{1,2,3})[1] = 9
}

It is ok to derive subslices from unaddressable slices, but not ok from unaddressable arrays. It is ok to take element addresses for unaddressable slices, but not ok for unaddressable arrays.

The reason is the same as the last detail.

An example:
package main

func main() {
	// Literal values and map elements are unaddressable in Go.

	// The following lines fail to compile.
	/*
	_ = [...]int{6, 7, 8, 9}[1:3]  // error: slice of unaddressable value
	_ = &([...]int{6, 7, 8, 9}[0]) // error: cannot take element address
	var ma = map[string][4]int{"abc": {0, 1, 2, 3}}
	_ = ma["abc"][1:3]  // error: slice of unaddressable value
	_ = &(ma["abc"][0]) // error: cannot take element address
	*/

	// The following lines compile okay.
	_ = []int{6, 7, 8, 9}[1:3]
	_ = &([]int{6, 7, 8, 9}[0])
	var ms = map[string][]int{"abc": {0, 1, 2, 3}}
	_ = ms["abc"][1:3]
	_ = &(ms["abc"][0])
}

NaN != NaN, Inf == Inf

This follows IEEE-754 standard and is consistent with most other programming languages:
package main

import "fmt"
import "math"

func main() {
	var a = math.Sqrt(-1.0)
	fmt.Println(a)      // NaN
	fmt.Println(a == a) // false

	var x = 0.0
	var y1, z1 = 1 / x, 2 / x
	var y2, z2 = -1 / x, -2 / x
	fmt.Println(y1, z1, y1 == z1) // +Inf +Inf true
	fmt.Println(y2, z2, y2 == z2) // +Inf +Inf true
}

Putting elements with NaN as key to a map is like putting the elements in a black hole

This reason is NaN != NaN (see the above detail). The elements with NaN as key can only be found out in a for-range loop.
package main

import "fmt"
import "math"

func main() {
	var a = math.NaN()
	fmt.Println(a)      // NaN

	var m = map[float64]int{}
	m[a] = 123
	v, present := m[a]
	fmt.Println(v, present) // 0 false
	m[a] = 789
	v, present = m[a]
	fmt.Println(v, present) // 0 false

	fmt.Println(m) // map[NaN:<nil> NaN:<nil>]
	delete(m, a) // no-op
	fmt.Println(m) // map[NaN:<nil> NaN:<nil>]

	for k, v := range m {
		fmt.Println(k, v)
	}
	// the above loop outputs:
	// NaN 123
	// NaN 789
}

For the standard Go compiler, the time complexity of comparing two equal interface values may be O(1) or O(n)

The time complexity depends on whether or not the direct parts of the two equal interfaces both reference the undrelying value. Please read the article value parts for detail.
package main

import "fmt"
import "time"

func main() {
	bigarr := [1 << 20]int{}

	type I interface{}

	// i0, i1 and i2 are three equal interfaces.
	var i0 I = bigarr // the dynamic value of i0 is a copy of bigarr.
	var i1 I = bigarr // the dynamic value of i1 is also a copy of bigarr.
	                  // Note, the dynamic values of i0 and i1 are
	                  // two different copies of bigarr.
	var i2 I = i1 // i2 shares the same dynamic value copy with i1.

	startTime := time.Now()
	_ = i0 == i1
	duration := time.Since(startTime)
	fmt.Println("duration for (i0 == i1):", duration)

	startTime = time.Now()
	_ = i1 == i2
	duration = time.Since(startTime)
	fmt.Println("duration for (i1 == i2):", duration)
}
/*
The output (1ms == 1,000,000ns):
duration for (i0 == i1): 1.381337ms
duration for (i1 == i2): 609ns
*/

1ms is 1000000ns!

About the time complexity of copying a non-nil interface value

The official Go FAQ says copying an interface value makes a copy of the thing stored in the interface value.. The article interfaces in Go mentions that, for the standard Go compiler, the time complexity of copying an interface value is O(1). But the the sizes of different dynamic values stored in interface values may be different, why isn't the complexity O(n)?

The reason is the dynamic values of interface values are all inmutable, so the standard Go compiler makes an optimization here. As the article value parts has explained, an non-nil interface value has an underlying part and the direct part of the interface value stores a pointer which references the underlying part. When the interface value is copied, only its direct part is copied. For the standard Go compiler, the sizes of the direct parts of all interface values are the same.

Constant key values in a container composite literal can't be duplicated

Duplicated non-constant keys are allowed. For example:
package main

import "fmt"

func main() {
	const N = 123
	var n = N

	var m = map[int]string {
		N: "abc",
		// N: "xyz", // error: duplicate key N in map literal
		n: "hello", // ok
		n: "bye", // ok
	}

	fmt.Println(m[N])
}

Please note, Go specification doesn't specifies the element initilization order in a composite literal. So the above program may print abc, hello or bye, depending on compilers and compiler versions.

Addresses of different zero-sized values may be equal, or not

Whether or not the addresses of two zero-sized values are equal is compiler and compiler version dependent.
package main

import "fmt"

func main() {
	a := struct{}{}
	b := struct{}{}
	x := struct{}{}
	y := struct{}{}
	m := [10]struct{}{}
	n := [10]struct{}{}
	o := [10]struct{}{}
	p := [10]struct{}{}

	fmt.Println(&x, &y, &o, &p)

	// For the standard Go compiler (1.10),
	// x, y, o and p escape to heap,
	// but a, b, m and n are allocated on stack.

	fmt.Println(&a == &b) // false
	fmt.Println(&x == &y) // true
	fmt.Println(&a == &x) // false

	fmt.Println(&m == &n) // false
	fmt.Println(&o == &p) // true
	fmt.Println(&n == &p) // false
}

The outputs indicated in the above code are for the standard Go compiler 1.10.

We should use os.IsNotExist(err) instead of err == os.ErrNotExist to check whether or not a file exists

Using err == os.ErrNotExist may miss errors.
package main

import (
	"fmt"
	"os"
)

func main() {
	_, err := os.Stat("a-nonexistent-file.abcxyz")
	fmt.Println(os.IsNotExist(err))    // true
	fmt.Println(err == os.ErrNotExist) // false
}

The base type of a pointer type may be the pointer type itself

An example:
package main

func main() {
	type P *P
	var p P
	p = &p
	p = **************p
}

Similarly,
package main

func main() {
	type S []S
	type M map[string]M
	type C chan C
	type F func(F) F

	s := S{0:nil}
	s[0] = s
	m := M{"Go": nil}
	m["Go"] = m
	c := make(C, 3)
	c <- c; c <- c; c <- c
	var f F
	f = func(F)F {return f}

	_ = s[0][0][0][0][0][0][0][0]
	_ = m["Go"]["Go"]["Go"]["Go"]
	<-<-<-c
	f(f(f(f(f))))
}

Parentheses are required in several rare scenarios to make code compile okay

For example:
package main

type T struct{x, y int}

func main() {
	// Each of the following three lines makes code fail to compile.
	// Some "{}" confuse compilers.
	/*
	if T{} == T{123, 789} {}
	if T{} == (T{123, 789}) {}
	if (T{}) == T{123, 789} {}
	*/

	// We must add parentheses like the following two lines to
	// make code compile okay.
	if (T{} == T{123, 789}) {}
	if (T{}) == (T{123, 789}) {}
}

The comment // import "x.y.z.mypkg" following package mypkg is meaningful for the standard Go compiler

For example, when the source files importing this package are compiled by the standard Go compiler, the import path of the following package must be "x.y.z.mypkg".
package mypkg // import "x.y.z.mypkg"
...

For a slice s, the loop for i = range s {...} is not equivalent to the loop for i = 0; i < len(s); i++ {...}

The respective final values of the iteration variable i may be different for the two loops.
package main

import "fmt"

var i int

func fa(s []int, n int) int {
	i = n
	for i = 0; i < len(s); i++ {}
	return i
}

func fb(s []int, n int) int {
	i = n
	for i = range s {}
	return i
}

func main() {
	s := []int{2, 3, 5, 7, 11, 13}
	fmt.Println(fa(s, -1), fb(s, -1)) // 6 5
	s = nil
	fmt.Println(fa(s, -1), fb(s, -1)) // 0 -1
}

A program doesn't need a main entry function to run

The following program runs well, at least for the standard Go compiler 1.10. (It may be not runnable since 1.11. Please read issue#21256 for details.)
package main

import (
    "fmt"
    "time"
)

func init() {
	for {
		time.Sleep(time.Second)
		fmt.Println("hi")
	}
}

var main int

A detail about selector shorthands

For a pointer value, which type is either defined or not, if the base type of its (pointer) type is a struct type, then the pointer value can access the fields of the struct value it references. However, if the type of the pointer value is a defined type, the value can not access the methods of the value its references.

package main

type T struct {
	x int
}
func (T) m(){} // T has one method.

type P *T  // a defined one-level pointer type.
type PP *P // a defined two-level pointer type.

func main() {
	var t T
	var tp = &t
	var tpp = &tp
	var p P = tp
	var pp PP = &p
	tp.x = 12  // okay
	p.x = 34   // okay
	pp.x = 56  // error: type PP has no field or method x
	tpp.x = 78 // error: type **T has no field or method x)

	tp.m()  // okay. Type *T also has a "m" method.
	p.m()   // error: type P has no field or method m
	pp.m()  // error: type PP has no field or method m
	tpp.m() // error: type **T has no field or method m
}

Non-exported method names and struct field names from different packages are viewed as diffferent names

For example, if the following types are declared in package foo:
package foo

type I = interface {
	about() string
}

type S struct {
	a string
}

func (s S) about() string {
	return s.a
}
and the following types are declared in package bar:
package bar

type I = interface {
	about() string
}

type S struct {
	a string
}

func (s S) about() string {
	return s.a
}
then,
package main

import "包2/foo"
import "包2/bar"

func main() {
	var x foo.S
	var y bar.S
	var _ foo.I = x
	var _ bar.I = y

	// The following lines fail to compile.
	x = foo.S(y)
	y = bar.S(x)
	var _ foo.I = y
	var _ bar.I = x
}

Two error values returned by two errors.New calls with the same argument are not equal

The reason is the errors.New function will copy the input string argument and use a pointer to the copied string as the dynamic value of the returned error value. Two different calls will produce two different pointers.
package main

import "fmt"
import "errors"

func main() {
	notfound := "not found"
	a, b := errors.New(notfound), errors.New(notfound)
	fmt.Println(a == b) // false
}

The flag standard package treats boolean command flags differently than integer and string flags

There are three forms to pass command arguments.
  1. -flag, for boolean flags only.
  2. -flag=x, for any flag.
  3. -flag x, for non-boolean flags only.

And please note that, all items following a boolean flag with the first form are viewed as arguments.

package main

import "fmt"
import "flag"

var b = flag.Bool("b", true, "a boolean flag")
var i = flag.Int("i", 123, "an integer flag")
var s = flag.String("s", "hi", "a string flag")

func main() {
	flag.Parse()
	fmt.Print("b=", *b, ", i=", *i, ", s=", *s, "\n")
	fmt.Println("arguments:", flag.Args())
}

If we run the following program with the below shown flags and arguments
./exampleProgram -b false -i 789 -s bye arg0 arg1
the output will be
b=true, i=123, s=hi
arguments: [false -i 789 -s bye arg0 arg1]

This output is obviously not what we expect.

We should pass the flags and arguments like
./exampleProgram -b=false -i 789 -s bye arg0 arg1
or
./exampleProgram -i 789 -s bye -b arg0 arg1
to get the output we expect:
b=true, i=789, s=bye
arguments: [arg0 arg1]

The capacity of the result slice of a conversion from a string to byte/rune slice may be larger than the length of the result slice

We should not assume the length and the capacity of the result slice are alway equal.

In the following example, if the last fmt.Println line is removed, the outputs of the two lines before it print the same value 32, otherwise, one print 32 and one print 8. (For the standard Go compiler 1.10.)
package main

import "fmt"

func main() {
	s := "a"
	x := []byte(s)              // len(s) == 1
	fmt.Println(cap([]byte(s))) // 32
	fmt.Println(cap(x))         // 8
	fmt.Println(x)
}

[Sp|Fp|P]rintf functions support positional arguments

The following program will print 3, 2, 1.

An example:
package main

import "fmt"

func main() {
	fmt.Printf("%[3]v, %[2]v, %[1]v", 1, 2, 3) // 3, 2, 1
}

Exit a program with a os.Exit function call and exit a goroutine with a runtime.Goexit function call

We can exit a program from any function by calling the os.Exit function. An os.Exit function call takes an int code as argument and returns the code to operating system.

An example:
// exit-example.go
package main

import "os"
import "time"

func main() {
	go func() {
		time.Sleep(time.Second)
		os.Exit(1)
	}()
	select{}
}
Run it:
$ go run a.go
exit status 1
$ echo $?
1

We can exit a goroutine by calling the runtime.Goexit function. The runtime.Goexit function has no parameters.

An example:
package main

import "fmt"
import "runtime"

func main() {
	c := make(chan int)
	go func() {
		defer func() {c <- 1}()
		defer fmt.Println("Go")
		func() {
			defer fmt.Println("C")
			runtime.Goexit()
		}()
		fmt.Println("Java")
	}()
	<-c
}

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.