현재 3권의 신간들인 Go Optimizations 101, Go Details & Tips 101Go Generics 101이 출간되어 있습니다. Leanpub 서점에서 번들을 모두 구입하시는 방법이 비용 대비 효율이 가장 좋습니다.

Go에 대한 많은 정보들과 Go 101 책들의 최신 소식을 얻으시려면 Go 101 트위터 계정인 @Go100and1을 팔로잉 해주세요.

Go Details 101

Index:

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-package 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
}

The default branch in switch and select blocks can be put before all case branches, after all case branches, or between case branches.

For example:
	switch n := rand.Intn(3); n {
	case 0: fmt.Println("n == 0")
	case 1: fmt.Println("n == 1")
	default: fmt.Println("n == 2")
	}

	switch n := rand.Intn(3); n {
	default: fmt.Println("n == 2")
	case 0: fmt.Println("n == 0")
	case 1: fmt.Println("n == 1")
	}

	switch n := rand.Intn(3); n {
	case 0: fmt.Println("n == 0")
	default: fmt.Println("n == 2")
	case 1: fmt.Println("n == 1")
	}

	var x, y chan int

	select {
	case <-x:
	case y <- 1:
	default:
	}

	select {
	case <-x:
	default:
	case y <- 1:
	}

	select {
	default:
	case <-x:
	case y <- 1:
	}

The numeric constant case expressions in a switch block can't be duplicate, but boolean ones can.

For example, the following program fails to compile.
package main

func main() {
	switch 123 {
	case 123:
	case 123: // error: duplicate case
	}
}
But the following program compiles okay.
package main

func main() {
	switch false {
	case false:
	case false:
	}
}

For reasons, please read this issue. The behavior is compiler dependent. In fact, the standard Go compiler also doesn't allow duplicate string case expressions, but gccgo allows.

The switch expressions in switch block are always evaluated to typed values.

For example, the switch expression 123 in the following switch block is viewed as a value of int instead of an untyped integer. So the following program fails to compile.
package main

func main() {
	switch 123 {
	case int64(123):  // error: mismatched types
	case uint32(789): // error: mismatched types
	}
}

The default switch expression of a switch block is a typed value true of the predeclared type bool.

For example, the following program will print true.
package main

import "fmt"

func main() {
	switch { // <=> switch true {
	case true:  fmt.Println("true")
	case false: fmt.Println("false")
	}
}

Sometimes, the open brace { of an explicit code block can be put on the next line.

For example:
package main

func main() {
	var i = 0
Outer:
	for
	{ // okay on the next line
		switch
		{ // okay on the next line
		case i == 5:
			break Outer
		default:
			i++
		}
	}
}

What result will the following program print? true or false? The answer is true. Please read line break rules in Go for reasons.
package main

import "fmt"

func False() bool {
	return false
}

func main() {
	switch False()
	{
	case true:  fmt.Println("true")
	case false: fmt.Println("false")
	}
}

Some case branch blocks must be explicit.

For example, the following program fails to compile.
func demo(n, m int) (r int) {
	switch n {
	case 123:
		if m > 0 {
			goto End
		}
		r++

		End: // syntax error: missing statement after label
	default:
		r = 1
	}
	return
}
To make it compile okay, the case branch code block should be explicit:
func demo(n, m int) (r int) {
	switch n {
	case 123: {
		if m > 0 {
			goto End
		}
		r++

		End:
	}
	default:
		r = 1
	}
	return
}
Alternatively, we can let a semicolon follow the label declaration End::
func demo(n, m int) (r int) {
	switch n {
	case 123:
		if m > 0 {
			goto End
		}
		r++

		End:;
	default:
		r = 1
	}
	return
}

Please read line break rules in Go for reasons.

A nested deferred function calls can modify return result values of its innermost nesting function.

For example:
package main

import "fmt"

func F() (r int) {
	defer func() {
		r = 789
	}()

	return 123 // <=> r = 123; return
}

func main() {
	fmt.Println(F()) // 789
}

Some recover calls may be no-ops.

We should call the recover function at the right places. Please read the right places to call the built-in recover function for details.

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 make a goroutine exit by calling the runtime.Goexit function. The runtime.Goexit function has no parameters.

In the following example, the Java word will not be printed.
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
}

The precedence of the increment operator ++ and the decrement -- is lower than the dereference operator * and the address-taking operator &, which are lower than the property selection operator . in selectors.

For example:
package main

import "fmt"

type T struct {
	x int
	y *int
}

func main() {
	var t T
	p := &t.x // <=> p := &(t.x)
	fmt.Printf("%T\n", p) // *int

	*p++ // <=> (*p)++
	*p-- // <=> (*p)--

	t.y = p
	a := *t.y // <=> *(t.y)
	fmt.Printf("%T\n", a) // int
}

The type deduction rule for the left untyped operand of a bit-shift operation depends on whether or not the right operand is a constant.

package main

func main() {
}

const M  = 2
// Compiles okay. 1.0 is deduced as an int value.
var _ = 1.0 << M

var N = 2
// Fails to compile. 1.0 is deduced as a float64 value.
var _ = 1.0 << N

Please read this article for reasons.

Values of two pointer types with different underlying types can be converted to each other if the base types of their underlying types share the same underlying type.

An example:
package main

type MyInt int64
type Ta    *int64
type Tb    *MyInt

func main() {
	var a Ta
	var b Tb

	// Direct conversion is not allowed.
	//a = Ta(b) // error

	// But indirect conversion is possible.
	y := (*MyInt)(b)
	x := (*int64)(y)
	a = x           // <=> the next line
	a = (*int64)(y) // <=> the next line
	a = (*int64)((*MyInt)(b))
	_ = a
}

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.20),
	// 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.20.

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,
  • the element type of a slice type can be the slice type itself,
  • the element type of a map type can be the map type itself,
  • the element type of a channel type can be the channel type itself,
  • and the argument and result types of a function type can be the function type itself.
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))))
}

A detail about selector shorthands.

For a pointer value, which type is either named or not, if the base type of its (pointer) type is a struct type, then we can select the fields of the struct value referenced by the pointer value through the pointer value. However, if the type of the pointer value is a named type, then we can't select the methods of the struct value referenced by the pointer value through the pointer value.

package main

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

type P *T  // a named one-level pointer type.
type PP *P // a named 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
}

Sometimes, nested composite literals can be simplified.

Please read nested composite literals can be simplified for details.

In some scenarios, it is ok to use array pointers as arrays.

Please read use array pointers as arrays for details.

Retrieving elements from nil maps will not panic. The result is a zero element value.

For example, the Foo1 and the Foo2 functions are equivalent, but the function Foo2 is much tidier than the function Foo1.
func Foo1(m map[string]int) int {
	if m != nil {
		return m["foo"]
	}
	return 0
}

func Foo2(m map[string]int) int {
	return m["foo"]
}

Deleting an entry from a nil map will not panic. It is a no-op.

For example, the following program will not panic.
package main

func main() {
	var m map[string]int // nil
	delete(m, "foo")
}

The result slice of an append function call may share some elements with the original slice, or not.

Please read append and delete container elements for details.

The length of a subslice may be larger than the base slice the subslice derives from.

For example,
package main

import "fmt"

func main() {
	s := make([]int, 3, 9)
	fmt.Println(len(s)) // 3
	s2 := s[2:7]
	fmt.Println(len(s2)) // 5
}

Please read derive slices from arrays and slices for details.

Deriving a subslice from a nil slice is ok if all the indexes used in the subslice expression are zero. The result subslice is also a nil slice.

For example, the following program will not panic at run time.
package main

import "fmt"

func main() {
	var x []int // nil
	a := x[:]
	b := x[0:0]
	c := x[:0:0]
	// Print three "true".
	fmt.Println(a == nil, b == nil, c == nil)
}

Please read derive slices from arrays and slices for details.

Ranging over a nil maps or a nil slices is ok, it is a no-op.

For example, the following program compiles okay.
package main

func main() {
	var s []int // nil
	for range s {
	}

	var m map[string]int // nil
	for range m {
	}
}

Range over a nil array pointer is ok if the second iteration variable is ignored or omitted.

For example, the following program will print 01234.
package main

import "fmt"

func main() {
	var a *[5]int // nil
	for i, _ := range a {
		fmt.Print(i)
	}
}

The length and capacity of a slice can be modified separately.

We can modify the length and capacity of a slice separately through the reflection way. Please read modify the length and capacity properties of a slice individually for details.

The indexes in slice and array composite literals must be constants and non-negative.

For example, the following code fails to compile.
var k = 1
// error: index must be non-negative integer constant
var x = [2]int{k: 1}
// error: index must be non-negative integer constant
var y = []int{k: 1}

Note, the keys in map composite literals are not required to be constants.

The constant indexes or keys in slice/array/map composite literals can't be duplicate.

For example, the following code fails to compile.
// error: duplicate index in array literal: 1
var a = []bool{0: false, 1: true, 1: true}
// error: duplicate index in array literal: 0
var b = [...]string{0: "foo", 1: "bar", 0: "foo"}
// error: duplicate key "foo" in map literal
var c = map[string]int{"foo": 1, "foo": 2}

This feature can be used to assert some conditions at compile time.

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 are unaddressable.

	// It is ok to take slice literal element addresses.
	_ = &[]int{1}[0] // ok
	// Cannot take addresses of array literal elements.
	_ = &[5]int{}[0] // error

	// It is ok to modify slice literal elements.
	[]int{1,2,3}[1] = 9  // ok
	// Cannot modify array literal elements.
	[3]int{1,2,3}[1] = 9 // error
}

It is ok to derive subslices from unaddressable slices, but not ok from unaddressable arrays.

The reason is the same as the last detail.

An example:
package main

func main() {
	// Map elements are unaddressable in Go.

	// The following lines compile okay. Deriving
	// slices from unaddressable slices is allowed.
	_ = []int{6, 7, 8, 9}[1:3]
	var ms = map[string][]int{"abc": {0, 1, 2, 3}}
	_ = ms["abc"][1:3]

	// The following lines fail to compile. Deriving
	// slices from unaddressable arrays is not allowed.
	/*
	_ = [...]int{6, 7, 8, 9}[1:3] // error
	var ma = map[string][4]int{"abc": {0, 1, 2, 3}}
	_ = ma["abc"][1:3]  // error
	*/
}

Putting entries with NaN as keys to a map is like putting the entries in a black hole.

This reason is NaN != NaN, which is another detail will be described below. Before Go 1.12, the elements with NaN as keys can only be found out in a for-range loop, Since Go 1.12, the elements with NaN as keys can also be printed out by fmt.Print alike functions.
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:789 NaN:123]
	delete(m, a)   // no-op
	fmt.Println(m) // map[NaN:789 NaN:123]

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

Please note, before Go 1.12, the two fmt.Println(m) calls both printed map[NaN:<nil> NaN:<nil>].

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 always 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.20).
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)
}

Some buggy code will be written if we assume the length and the capacity of the result slice are always equal.

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
}

The iteration order over maps is not guaranteed to be the same from one iteration to the next.

For example, the following program will not run infinitely:
package main

import "fmt"

func f(m map[byte]byte) string {
	bs := make([]byte, 0, 2*len(m))
	for k, v := range m {
		bs = append(bs, k, v)
	}
	return string(bs)
}

func main() {
	m := map[byte]byte{'a':'A', 'b':'B', 'c':'C'}
	s0 := f(m)
	for i := 1; ; i++{
		if s := f(m); s != s0 {
			fmt.Println(s0)
			fmt.Println(s)
			fmt.Println(i)
			return
		}
	}
}

Please note, the entries in the JSON marshal result on maps are sorted by their keys. And since Go 1.12, printing maps (with the print functions in the standard fmt package) also results sorted entries.

If a map entry is created during an iteration of the map, that entry may be iterated during the iteration or may be skipped.

A proof:
package main

import "fmt"

func main() {
	m := map[int]int{0: 0, 1: 100, 2: 200}
	r, n, i:= len(m), len(m), 0
	for range m {
		m[n] = n*100
		n++
		i++
	}
	fmt.Printf("%d new entries, iterate %d and skip %d\n",
		i, i - r, n - i,
	)
}

Thanks to Valentin Deleplace for the above two detail suggestions.

A multi-result function call can't mix with other expressions when the call is used as the sources in an assignment or the arguments of another function call.

Please read use function calls as expressions for details.

Some function calls are evaluated at compile time.

Please read some function calls are evaluated at compile time for details.

Each method corresponds to an implicit function.

Please read each Method Corresponds to an Implicit Function for details.

Comparing two interface values with the same dynamic incomparable type produces a panic.

For example:
package main

func main() {
	var x interface{} = []int{}
	_ = x == x // panic
}

Type assertions can be used to convert a value of an interface type to another interface type, even if the former interface type doesn't implement the latter one.

For example:
package main

type Foo interface {
	foo()
}

type T int
func (T) foo() {}

func main() {
	var x interface{} = T(123)
	// The following two lines fails to compile, for the
	// same reason: interface{} does not implement Foo.
	/*
	var _ Foo = x   // error
	var _ = Foo(x)  // error
	*/
	// But the following line compiles and runs okay.
	var _ = x.(Foo) // okay
}

Whether or not the second optional result of a type assertion is present will affect the behavior of the type assertion.

If the second optional result presents in a failed type assertion, the type assertion will not produce a panic. Otherwise, a panic will occur. For example:
package main

func main() {
	var x interface{} = true

	// Assertion fails, but doesn't cause a panic.
	_, _ = x.(int)

	// Assertion fails, which causes a panic.
	_ = x.(int)
}

About the impossible to-interface assertions which can be detected at compile time.

At compile time, some to-interface assertions can be deducted as impossible to succeed. For example, the assertion shown in the following code:
package main

type Ia interface {
	m()
}

type Ib interface {
	m() int
}

type T struct{}

func (T) m() {}

func main() {
	var x Ia = T{}
	_ = x.(Ib) // panic: main.T is not main.Ib
}

Such assertions will not make code compilations fail (but the program will panic at run time). Since Go Toolchain 1.15, the go vet command warns on such assertions.

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
}

Receive-only channels can't be closed.

For example, the following code fails to compile.
package main

func main() {
}

func foo(c <-chan int) {
	close(c) // error: cannot close receive-only channel
}

Sending a value to a closed channel is viewed as a non-blocking operation, and this operation causes a panic.

For example, in the following program, when the second case branch gets selected, it will produce a panic at run time.
package main

func main() {
	var c = make(chan bool)
	close(c)
	select {
	case <-c:
	case c <- true: // panic: send on closed channel
	default:
	}
}

Types can be declared within function bodies.

Types can be declared in function bodies. For example,
package main

func main() {
	type T struct{}
	type S = []int
}

For the standard compiler, zero-sized fields in a struct may be treated as one-byte-sized value.

Please read this FAQ item for details.

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 y = 1.0 / x
	var z = 2.0 * y
	fmt.Println(y, z, y == z) // +Inf +Inf true
}

Non-exported method names and struct field names from different packages are viewed as different 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,
  • values of the two respective types S from the two packages can't be converted to each other.
  • the two respective interface types S from the two packages denote two distinct method sets.
  • type foo.S doesn't implement the interface type bar.I.
  • type bar.S doesn't implement the interface type foo.I.
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
}

In struct value comparisons, blank fields will be ignored.

Blank fields are those fields whose name are the blank identifier _. The following program will print true.
package main

import "fmt"

type T struct {
	_ int
	_ bool
}

func main() {
	var t1 = T{123, true}
	var t2 = T{789, false}
	fmt.Println(t1 == t2) // true
}

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 "{}"s confuse compilers.
	/*
	if T{} == T{123, 789} {}
	if T{} == (T{123, 789}) {}
	if (T{}) == T{123, 789} {}
	var _ = func()(nil) // nil is viewed as a type
	*/

	// We must add parentheses like the following
	// two lines to make code compile okay.
	if (T{} == T{123, 789}) {}
	if (T{}) == (T{123, 789}) {}
	var _ = (func())(nil) // nil is viewed as a value
}

Stack overflow is not panic.

For the current main stream Go compilers, stack overflows are fatal errors. Once a stack overflow happens, the whole program will crash without recovery ways.
package main

func f() {
	f()
}

func main() {
	defer func() {
		recover() // helpless to avoid program crashing
	}()
	f()
}
the running result:
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
...

About more crash cases, please read this wiki article.

Some expression evaluation orders in Go are compiler implementation dependent.

Please read expression evaluation orders in Go for details.

The results of reflect.DeepEqual(x, y) and x == y may be different.

The function call reflect.DeepEqual(x, y) will always return false if the types of its two arguments are different, whereas x == y may return true even if the types of the two operands are different.

The second difference is a DeepEqual call with two pointer argument values of the same type returns whether or not the two respective values referenced by the two pointers are deep equal. So the call might return true even if the two pointers are not equal.

The third difference is the result of a DeepEqual call might return true if both of its arguments are in cyclic reference chains (to avoid infinite looping in the call).

The fourth difference is, the function call reflect.DeepEqual(x, y) is not expected to panic generally, whereas x == y will panic if the two operands are both interface values and their dynamic types are identical and incomparable.

An example showing these differences:
package main

import (
	"fmt"
	"reflect"
)

func main() {
	type Book struct {page int}
	x := struct {page int}{123}
	y := Book{123}
	fmt.Println(reflect.DeepEqual(x, y)) // false
	fmt.Println(x == y)                  // true

	z := Book{123}
	fmt.Println(reflect.DeepEqual(&z, &y)) // true
	fmt.Println(&z == &y)                  // false

	type Node struct{peer *Node}
	var q, r, s Node
	q.peer = &q // form a cyclic reference chain
	r.peer = &s // form a cyclic reference chain
	s.peer = &r
	println(reflect.DeepEqual(&q, &r)) // true
	fmt.Println(q == r)                // false

	var f1, f2 func() = nil, func(){}
	fmt.Println(reflect.DeepEqual(f1, f1)) // true
	fmt.Println(reflect.DeepEqual(f2, f2)) // false

	var a, b interface{} = []int{1, 2}, []int{1, 2}
	fmt.Println(reflect.DeepEqual(a, b)) // true
	fmt.Println(a == b)                  // panic
}

Note, if the two arguments of a DeepEqual call are both function values, then the call returns true only if the two function arguments are both nil and their types are identical. It is similar to compare container values whose elements contain function values or compare struct values whose fields contain function values. But please also note that the result of comparing two slices (of the same type) is always true if the two slices exactly share the same elements (in other words, they have the same length and each pair of their corresponding elements have the same address). An example:
package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := [1]func(){func(){}}
	b := a
	fmt.Println(reflect.DeepEqual(a, a))       // false
	fmt.Println(reflect.DeepEqual(a[:], a[:])) // true
	fmt.Println(reflect.DeepEqual(a[:], b[:])) // false
	a[0], b[0] = nil, nil
	fmt.Println(reflect.DeepEqual(a[:], b[:])) // true
}

The reflect.Value.Bytes() method returns a []byte value, which element type, byte, might be not the same as the Go slice value represented by the receiver parameter.

Assume the underlying type of a defined type MyByte is the predeclared type byte, we know that Go type system forbids the conversions between []MyByte and []byte values. However, it looks the implementation of the method Bytes of the reflect.Value type partially violates this restriction unintentionally, by allowing converting a []MyByte value to []byte.

Example:
package main

import "bytes"
import "fmt"
import "reflect"

type MyByte byte

func main() {
	var mybs = []MyByte{'a', 'b', 'c'}
	var bs []byte

	// bs = []byte(mybs) // this line fails to compile

	v := reflect.ValueOf(mybs)
	bs = v.Bytes() // okay. Violating Go type system.
	fmt.Println(bytes.HasPrefix(bs, []byte{'a', 'b'})) // true

	bs[1], bs[2] = 'r', 't'
	fmt.Printf("%s \n", mybs) // art
}

But it looks the violation is not harmful. On the contrary, it makes some benefits. For example, with this violation, we can use the functions in the bytes standard package for the []MyByte values.

Note, the reflect.Value.Bytes() method might be removed later.

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
}

For projects only supporting Go 1.13+, errors.Is(err, os.ErrNotExist) is more recommended to be used to check whether or not a file exists.
package main

import (
	"errors"
	"fmt"
	"os"
)

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

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

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

And please note that, a boolean flag with the first form is viewed as the last flag, all items following it 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 this 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]

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

The following program will print coco.
package main

import "fmt"

func main() {
	// The next line prints: coco
	fmt.Printf("%[2]v%[1]v%[2]v%[1]v", "o", "c")
}


Index↡

The Go 101 프로젝트는 Github 에서 호스팅됩니다. 오타, 문법 오류, 부정확한 표현, 설명 결함, 코드 버그, 끊어진 링크와 같은 모든 종류의 실수에 대한 수정 사항을 제출하여 Go 101을 개선을 돕는 것은 언제나 환영합니다.

주기적으로 Go에 대한 깊이 있는 정보를 얻고 싶다면 Go 101의 공식 트위터 계정인 @go100and1을 팔로우하거나 Go 101 슬랙 채널에j가입해주세요.

이 책의 디지털 버전은 아래와 같은 곳을 통해서 구매할 수 있습니다.
Go 101의 저자인 Tapir는 2016년 7월부터 Go 101 시리즈 책들을 집필하고 go101.org 웹사이트를 유지 관리하고 있습니다. 새로운 콘텐츠는 책과 웹사이트에 수시로 추가될 예정입니다. Tapir는 인디 게임 개발자이기도 합니다. Tapir의 게임을 플레이하여 Go 101을 지원할 수도 있습니다. (안드로이드와 아이폰/아이패드용):
  • Color Infection (★★★★★), 140개 이상의 단계로 이루어진 물리 기반의 캐주얼 퍼즐 게임
  • Rectangle Pushers (★★★★★), 2가지 모드와 104개 이상의 단계로 이루어진 캐주얼 퍼즐 게임
  • Let's Play With Particles, 세가지 미니 게임이 있는 캐주얼 액션 게임
페이팔을 통한 개인 기부도 환영합니다.

색인: