Jinyun's Notes 🚀

没什么天赋,爱好也不多,但愿坚持做些喜欢的事情

0%

Go 正确姿势

🙏 在编写 Go 代码之时,我跟很多新手一样踩了不少坑,感觉非常有必要将那些踩过的坑记录下来以避免下次犯错。很多人说 Go 简单易学,上手容易,可惜我不是那些很多人中的一个,天资愚钝如此,我只能反复学习了。

常见错误与纠正

assignment to entry in nil map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package hello

import (
"testing"
)

/*
=== RUN TestWrongUsage
--- FAIL: TestWrongUsage (0.00s)
panic: assignment to entry in nil map [recovered]
panic: assignment to entry in nil map
*/
func TestWrongUsage(t *testing.T) {
var m map[string]float64
m["pi"] = 3.1416
t.Log(m)
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:39: map[pi:3.1416]
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.012s
*/
func TestRightUsage(t *testing.T) {
m := make(map[string]float64)
m["pi"] = 3.1416
t.Log(m)
}

invalid memory address or nil pointer dereference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package hello

import (
"math"
"testing"
)

type Point struct {
X, Y float64
}

func (p *Point) Abs() float64 {
return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

/*
=== RUN TestWrongUsage
--- FAIL: TestWrongUsage (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x110a643]
*/
func TestWrongUsage(t *testing.T) {
var p *Point
t.Log(p.Abs())
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:26: &{0 0}
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:29: 0
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.009s
*/
func TestRightUsage(t *testing.T) {
var p *Point = new(Point)
t.Log(p)

var q Point // has zero value Point{X:0, Y:0}
t.Log(q.Abs())
}

array won’t change

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package hello

import (
"testing"
)

func Foo(a [2]int) {
a[0] = 8
}

func Bar(a []int) {
if len(a) > 0 {
a[0] = 8
}
}

/*
=== RUN TestWrongUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: [1 2]
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello 0.008s
*/
func TestWrongUsage(t *testing.T) {
a := [2]int{1, 2}
Foo(a)
t.Log(a)
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:40: [8 2]
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.013s
*/
func TestRightUsage(t *testing.T) {
a := []int{1, 2}
Bar(a)
t.Log(a)
}

shadowed variables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package hello

import (
"testing"
)

/*
=== RUN TestWrongUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: 0
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello 0.007s
*/
func TestWrongUsage(t *testing.T) {
n := 0
if true {
n := 1
n++
}
t.Log(n)
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:36: 2
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello (cached)
*/
func TestRightUsage(t *testing.T) {
n := 0
if true {
n = 1
n++
}
t.Log(n)
}

安装 Go Tools

1
$ go get -u golang.org/x/tools/...

检测隐藏变量:

1
2
3
$ go vet -vettool=$(which shadow) -strict
# example.com/hello
./hello_test.go:17:3: declaration of "n" shadows declaration at line 15

immutable strings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package hello

import (
"testing"
)

/*
# example.com/hello [example.com/hello.test]
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:16:7: cannot assign to s[0] (strings are immutable)
FAIL example.com/hello [build failed]
*/
func TestWrongUsage(t *testing.T) {
s := "hello"
s[0] = 'H' // IDE 会提示:cannot assign to s[0] (value of type byte)compilerUnassignableOperand
t.Log(s)
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:29: Hello
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.007s
*/
func TestRightUsage(t *testing.T) {
buf := []rune("hello")
buf[0] = 'H'
s := string(buf)
t.Log(s)
}

characters add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package hello

import (
"fmt"
"strconv"
"testing"
)

/*
=== RUN TestWrongUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:13: Ta
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:14: 181
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello 0.014s
*/
func TestWrongUsage(t *testing.T) {
t.Log("T" + "a")
t.Log('T' + 'a')
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/imajinyun/hello/hello_test.go:30: 84a
/Users/xxx/Codes/go/src/github.com/imajinyun/hello/hello_test.go:31: Ta
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.012s
PASS
ok example.com/hello 0.012s
*/
func TestRightUsage(t *testing.T) {
t.Log(strconv.Itoa(84) + string('a'))
t.Log(fmt.Sprintf("%c%c", 84, 'a'))
}

trim string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package hello

import (
"strings"
"testing"
)

/*
=== RUN TestWrongUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:17: true
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: true
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello 0.009s
*/
func TestWrongUsage(t *testing.T) {
t.Log(" hello world " == strings.TrimRight(" hello world ", "hello"))
t.Log("hello world" == strings.TrimLeft("hello world", "world"))
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:33: true
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:34: true
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:35: true
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:36: true
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.008s
*/
func TestRightUsage(t *testing.T) {
t.Log("hello" == strings.TrimSpace(" hello "))
t.Log("hello \t world" == strings.TrimSpace(" \t hello \t world \t "))
t.Log(" world" == strings.TrimPrefix("hello world", "hello"))
t.Log("hello " == strings.TrimSuffix("hello world", "world"))
}

elements copy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package hello

import (
"testing"
)

/*
=== RUN TestWrongUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: dst: []
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello (cached)
*/
func TestWrongUsage(t *testing.T) {
var src, dst []int
src = []int{1, 2, 3}
copy(dst, src) // Copy elements to dst from src.
t.Log("dst:", dst)
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:36: dst: [1 2 3] (copied 3 numbers)
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.008s
*/
func TestRightUsage(t *testing.T) {
var src, dst []int
src = []int{1, 2, 3}
dst = make([]int, len(src))
n := copy(dst, src)
t.Log("dst:", dst, "(copied", n, "numbers)")
}

/*
=== RUN TestRight2Usage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:40: dst: [1 2 3]
--- PASS: TestRight2Usage (0.00s)
PASS
ok example.com/hello 0.016s
*/
func TestRight2Usage(t *testing.T) {
var src, dst []int
src = []int{1, 2, 3}
dst = append(dst, src...)
t.Log("dst:", dst)
}

can’t change entries in range loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package hello

import "testing"

/*
=== RUN TestWrongUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:21: [1 1 1]
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello 0.011s
*/
func TestWrongUsage(t *testing.T) {
s := []int{1, 1, 1}
for _, n := range s {
n += 1
}
t.Log(s)
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:36: [2 2 2]
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.013s
*/
func TestRightUsage(t *testing.T) {
s := []int{1, 1, 1}
for i := range s {
s[i] += 1
}
t.Log(s)
}

iteration variable doesn’t see change in range loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package hello

import (
"fmt"
"testing"
)

/*
=== RUN TestWrongUsage
x = 0
x = 0
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:21: [0 8]
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello 0.013s
*/
func TestWrongUsage(t *testing.T) {
var a [2]int
for _, x := range a {
fmt.Println("x =", x)
a[1] = 8
}
t.Log(a)
}

/*
=== RUN TestRightUsage
x = 0
x = 8
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:39: [0 8]
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.009s
*/
func TestRightUsage(t *testing.T) {
var a [2]int
for _, x := range a[:] {
fmt.Println("x =", x)
a[1] = 8
}
t.Log(a)
}

iteration variables and closures

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package hello

import (
"sync"
"testing"
)

/*
=== RUN TestWrongUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:23: 5
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:23: 5
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:23: 5
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:23: 5
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:23: 5
--- PASS: TestWrongUsage (0.00s)
PASS
ok example.com/hello 0.013s
*/
func TestWrongUsage(t *testing.T) {
var wg sync.WaitGroup
var n int = 5
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
t.Log(i)
wg.Done()
}()
}
wg.Wait()
}

/*
=== RUN TestRightUsage
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:47: 4
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:47: 1
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:47: 0
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:47: 3
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:47: 2
--- PASS: TestRightUsage (0.00s)
PASS
ok example.com/hello 0.009s
*/
func TestRightUsage(t *testing.T) {
var wg sync.WaitGroup
var n int = 5
wg.Add(n)
for i := 0; i < n; i++ {
x := i // Create a unique variable for each closure.
go func() {
t.Log(x)
wg.Done()
}()
}
wg.Wait()
}

常用的一些写法

检查字典中是否存在指定键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package hello

import "testing"

/*
=== RUN TestIfKeyExistInMap
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:12: value: 100
--- PASS: TestIfKeyExistInMap (0.00s)
PASS
ok example.com/hello 0.009s
*/
func TestIfKeyExistInMap(t *testing.T) {
dict := map[string]int{"foo": 100, "bar": 200}
value, ok := dict["foo"]
if ok {
t.Log("value:", value)
} else {
t.Log("Key not found")
}
}

连接两个切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package hello

import "testing"

/*
=== RUN TestCombineTwoSlice
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:17: [1 2 3 4]
--- PASS: TestCombineTwoSlice (0.00s)
PASS
ok example.com/hello 0.007s
*/
func TestCombineTwoSlice(t *testing.T) {
slice := append([]int{1, 2}, []int{3, 4}...)
t.Log(slice)
}

将 byte 转换为 string

标准转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package hello

import "testing"

/*
=== RUN TestByteToStr
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:16:
[72 101 108 108 111 32 87 111 114 108 100]
Hello World
--- PASS: TestByteToStr (0.00s)
PASS
ok example.com/hello 0.007s
*/
func TestByteToStr(t *testing.T) {
b := []byte("Hello World")
t.Logf("\n%v\n%v", b, string(b))
}

强制转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package hello

import (
"reflect"
"testing"
"unsafe"
)

/*
=== RUN TestByteToString
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:19:
[72 101 108 108 111 32 87 111 114 108 100]
"Hello World"
Hello World
--- PASS: TestByteToString (0.00s)
PASS
ok example.com/hello 0.008s
*/
func TestByteToString(t *testing.T) {
b := []byte{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100}
s := byteToString(b)
t.Logf("\n%v\n%#v\n%v", b, s, s)
}

func byteToString(b []byte) (s string) {
data := make([]byte, len(b))
for i, c := range b {
data[i] = c
}
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(&data[0]))
hdr.Len = len(b)
return s
}

将 string 转换为 byte

标准转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package hello

import "testing"

/*
=== RUN TestStringToByte
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18:
Hello World
[72 101 108 108 111 32 87 111 114 108 100]
--- PASS: TestStringToByte (0.00s)
PASS
ok example.com/hello 0.008s
*/
func TestStringToByte(t *testing.T) {
s := "Hello World"
t.Logf("\n%v\n%v", s, []byte(s))
}

强制转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package hello

import "testing"

/*
=== RUN TestStringToByte
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:19:
Hello World
[72 101 108 108 111 32 87 111 114 108 100]
--- PASS: TestStringToByte (0.00s)
PASS
ok example.com/hello 0.012s
*/
func TestStringToByte(t *testing.T) {
s := "Hello World"
b := stringToByte(s)
t.Logf("\n%v\n%v\n", s, b)
}

func stringToByte(s string) []byte {
b := make([]byte, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
b[i] = c
}
return b
}

将 rune 转换为 string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package hello

import "testing"

/*
=== RUN TestRuneToString
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:8:
r = [72 101 108 108 111 32 87 111 114 108 100 33]
r = [U+0048 U+0065 U+006C U+006C U+006F U+0020 U+0057 U+006F U+0072 U+006C U+0064 U+0021]
s = Hello World!
--- PASS: TestRuneToString (0.00s)
PASS
ok example.com/hello
*/
func TestRuneToString(t *testing.T) {
r := []rune{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33}
s := string(r)
t.Logf("\nr = %v\nr = %U\ns = %v", r, r, s)
}

将 string 转换为 rune

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package hello

import "testing"

/*
=== RUN TestStringToRune
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:14:
s = Hello World!
r = [72 101 108 108 111 32 87 111 114 108 100 33]
r = [U+0048 U+0065 U+006C U+006C U+006F U+0020 U+0057 U+006F U+0072 U+006C U+0064 U+0021]
--- PASS: TestStringToRune (0.00s)
PASS
ok example.com/hello 0.016s
*/
func TestStringToRune(t *testing.T) {
s := "Hello World!"
r := []rune(s)
t.Logf("\ns = %v\nr = %v\nr = %U", s, r, r)
}

结构体比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package hello

import (
"reflect"
"testing"
)

type Devloper struct {
Name string
Lang string
Age int
}

/*
=== RUN TestCompareStruct
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: false
/Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:19: true
--- PASS: TestCompareStruct (0.00s)
PASS
ok example.com/hello
*/
func TestCompareStruct(t *testing.T) {
d1 := Devloper{"Foo", "Java", 26}
d2 := Devloper{"Bar", "Go", 28}
d3 := Devloper{"Bar", "Go", 28}
t.Log(reflect.DeepEqual(d1, d2))
t.Log(reflect.DeepEqual(d2, d3))
}
本笔记是笔者在学习和工作中的一些整理,如对您有用,请鼓励我继续写作