在编写 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))
}
|
How to Properly Hash and Salt Passwords in Golang Using Bcrypt
Download the golang bcrypt library using go get golang.org/x/crypto/bcrypt
.
功能代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| package example
import (
"golang.org/x/crypto/bcrypt"
)
// Hash password using the bcrypt hashing algorithm.
func genHashPassword(password string) (string, error) {
// Convert password string to byte slice.
var passwordBytes = []byte(password)
// Hash password with bcrypt's min cost.
hashedPasswordBytes, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
return string(hashedPasswordBytes), err
}
// Check if two passwords match using Bcrypt's CompareHashAndPassword
// which return nil on success and an error on failure.
func cmpHashPassword(hashedPassword, currPassword string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(currPassword))
return err == nil
}
|
测试代码:
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 example
import (
"testing"
)
func TestHashPassword(t *testing.T) {
var hashPassword, err = genHashPassword("mypassword")
if err != nil {
t.Error("Failed to hash password")
return
}
t.Logf("Get hashed password: %v", hashPassword)
}
func TestCmpHashPassword(t *testing.T) {
var currPassword = "mypassword"
var hashPassword, err = genHashPassword(currPassword)
if err != nil {
t.Error("Failed to hash password")
return
}
t.Logf("Get hashed password: %v", hashPassword)
t.Logf("Cmp hashed password: %v", cmpHashPassword(hashPassword, currPassword))
}
/*
Running tool: /opt/homebrew/bin/go test -timeout 30s -coverprofile=/var/folders/nk/dtkbhx993b57y5kt49l4qsb40000gn/T/vscode-goOPwBHl/go-code-cover example -v
=== RUN TestHashPassword
/Users/xxx/Codes/github.com/xxx/inotes/vendor/acme_test.go:14: Get hashed password: $2a$10$L/tY5of7u9FOw94y7PrKR.3irKHtBattazT/JzypTcg5e25Kzu3vG
--- PASS: TestHashPassword (0.07s)
=== RUN TestCmpHashPassword
/Users/xxx/Codes/github.com/xxx/inotes/vendor/acme_test.go:24: Get hashed password: $2a$10$b2fKxBL0FQm0/EOE6nLFe.h6X/Xc367jrqwLyTGonS5Ot78AG6V62
/Users/xxx/Codes/github.com/xxx/inotes/vendor/acme_test.go:25: Cmp hashed password: true
--- PASS: TestCmpHashPassword (0.14s)
PASS
coverage: 100.0% of statements
ok example 0.531s coverage: 100.0% of statements
*/
|