default
branch in switch
and select
blocks can be put before all case
branches, after all case
branches, or between case
branches.
switch
block can't be duplicate, but boolean ones can.
switch
block are always evaluated to typed values.
switch
block is a typed value true
of the predeclared type bool
.
{
of an explicit code block can be put on the next line.
case
branch blocks must be explicit.
recover
calls may be no-ops.
os.Exit
function call and exit a goroutine with a runtime.Goexit
function call.
++
and the decrement --
is lower than the dereference operator *
and the address-taking operator &
, which are lower than the property selection operator .
in selectors.
append
function call may share some elements with the original slice, or not.
NaN
as keys to a map is like putting the entries in a black hole.
s
, the loop for i = range s {...}
is not equivalent to the loop for i = 0; i < len(s); i++ {...}
.
error
values returned by two errors.New
calls with the same argument are not equal.
reflect.DeepEqual(x, y)
and x == y
may be different.
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.
os.IsNotExist(err)
instead of err == os.ErrNotExist
to check whether or not a file exists.
flag
standard package treats boolean command flags differently than number and string flags.
[Sp|Fp|P]rintf
functions support positional arguments.
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
}
default
branch in switch
and select
blocks can be put before all case
branches, after all case
branches, or between case
branches. 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:
}
switch
block can't be duplicate, but boolean ones can.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.
switch
block are always evaluated to typed values.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
}
}
switch
block is a typed value true
of the predeclared type bool
.true
.
package main
import "fmt"
func main() {
switch { // <=> switch true {
case true: fmt.Println("true")
case false: fmt.Println("false")
}
}
{
of an explicit code block can be put on the next line.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")
}
}
case
branch blocks must be explicit.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.
package main
import "fmt"
func F() (r int) {
defer func() {
r = 789
}()
return 123 // <=> r = 123; return
}
func main() {
fmt.Println(F()) // 789
}
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.
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.
// 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.
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
}
++
and the decrement --
is lower than the dereference operator *
and the address-taking operator &
, which are lower than the property selection operator .
in selectors.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
}
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.
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
}
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.
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))))
}
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
}
Please read nested composite literals can be simplified for details.
Please read use array pointers as arrays for details.
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"]
}
package main
func main() {
var m map[string]int // nil
delete(m, "foo")
}
append
function call may share some elements with the original slice, or not.Please read append and delete container elements for details.
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.
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.
package main
func main() {
var s []int // nil
for range s {
}
var m map[string]int // nil
for range m {
}
}
01234
.
package main
import "fmt"
func main() {
var a *[5]int // nil
for i, _ := range a {
fmt.Print(i)
}
}
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.
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.
// 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.
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
}
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
*/
}
NaN
as keys to a map is like putting the entries in a black hole.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>]
.
We should not assume the length and the capacity of the result slice are always equal.
In the following example, if the lastfmt.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.
s
, the loop for i = range s {...}
is not equivalent to the loop for i = 0; i < len(s); i++ {...}
.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
}
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.
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.
Please read use function calls as expressions for details.
Please read some function calls are evaluated at compile time for details.
Please read each Method Corresponds to an Implicit Function for details.
package main
func main() {
var x interface{} = []int{}
_ = x == x // panic
}
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
}
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)
}
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.
error
values returned by two errors.New
calls with the same argument are not equal.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
}
package main
func main() {
}
func foo(c <-chan int) {
close(c) // error: cannot close receive-only channel
}
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:
}
}
package main
func main() {
type T struct{}
type S = []int
}
Please read this FAQ item for details.
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
}
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,
S
from the two packages
can't be converted to each other.
S
from the two packages
denote two distinct method sets.
foo.S
doesn't implement the interface type bar.I
.
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
}
_
.
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
}
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
}
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.
Please read expression evaluation orders in Go for details.
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.
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
}
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
.
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.
os.IsNotExist(err)
instead of err == os.ErrNotExist
to check whether or not a file exists.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
}
flag
standard package treats boolean command flags differently than integer and string flags.-flag
, for boolean flags only.
-flag=x
, for any flag.
-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.coco
.
package main
import "fmt"
func main() {
// The next line prints: coco
fmt.Printf("%[2]v%[1]v%[2]v%[1]v", "o", "c")
}
The Go 101 프로젝트는 Github 에서 호스팅됩니다. 오타, 문법 오류, 부정확한 표현, 설명 결함, 코드 버그, 끊어진 링크와 같은 모든 종류의 실수에 대한 수정 사항을 제출하여 Go 101을 개선을 돕는 것은 언제나 환영합니다.
주기적으로 Go에 대한 깊이 있는 정보를 얻고 싶다면 Go 101의 공식 트위터 계정인 @go100and1을 팔로우하거나 Go 101 슬랙 채널에j가입해주세요.
reflect
표준 패키지sync
표준 패키지sync/atomic
표준 패키지nil