Go的坑点记录 (持续记录)

Go的变量传参是值传递

这边文章有具体分析:《Go语言参数传递是传值还是传引用》,看一段代码:

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

import "fmt"

func main() {

var i = make([]int, 3)
foo(i)
fmt.Println(i)
boo(i)
// 没有输出 1 0 0 3 4 5
fmt.Println(i)
}

func foo(s []int) {
s[0] = 1
}

func boo(s []int) {
s = append(s, 3, 4, 5)
}

输出:

1
2
[1 0 0]
[1 0 0]

第二段输出并不是[1 0 0 3 4 5],切片是引用类型,但是并不是引用传递。Go底层中如果切片没有扩容,修改的结果将应用到原值。

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


## Defer 中包含panic,panic仅有最后一个可以被revover捕获

示例代码:

```go
package main

import (
"fmt"
)

func main() {

defer func() {
// 无法被捕获
panic("defer panic 1")
}()

defer func() {
if err := recover(); err != nil{
fmt.Println("receive: ",err)
}else {
fmt.Println("fatal")
}
}()

defer func() {
// 被recover捕获
panic("defer panic 2")
}()

defer func() {
panic("defer panic 3")
}()

// 这里的会被替换
panic("panic here")
}

defer的执行顺序是先进后出。输出:

1
2
3
4
5
6
7
8
receive:  defer panic 2
panic: defer panic 1

goroutine 1 [running]:
main.main.func1()
/Users/xxx/workspace/testing/go/main.go:11 +0x39
main.main()
/Users/xxx/workspace/testing/go/main.go:32 +0x99

触发panic("panic")后defer顺序出栈执行,第一个被执行的defer中 会有panic("defer panic 3")异常语句,这个异常将会覆盖掉main中的异常panic("panic"),第三个defer会替换掉panic(“defer panic 3”),这个异常被第二个执行的defer捕获到。第一个无法被捕获程序崩溃。

Go中可以调用类型赋值为nil的属性方法

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

import "fmt"

type Foo struct {

}

func (f *Foo) A() {
fmt.Println("A")
}

func main() {
var f = new(Foo)
fmt.Println(f)
f = nil
f.A()
}

正常输出:

1
2
&{}
A

程序并不会奔溃。这事什么原因呢?Google中找到一段说明

In Go the function to be called by the Expression.Name() syntax is entirely determined by the type of Expression and not by the particular run-time value of that expression, including nil.

对象方法是不在运行时确定的(编译时已经确定),所以运行时对象的nil,并不会导致panic;

struct{} 是个空类型

struct{} 是个空类型,所有struct{} 指向同一个零地址,可以用于channel的信号传送

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

type foo1 struct{}
type foo2 struct{}

func main() {
f1 := new(foo1)
f2 := new(foo2)
fmt.Printf("%p\n", f1)
fmt.Printf("%p", f2)
}

无论执行多少次,同台电脑都会输出相同的值

1
2
0x118e370
0x118e370

不要用共享内存进行协程通讯

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 main

import (
"fmt"
"time"
)

func main() {

you := "bar are you"
foo := "foo"

str := ""
go func() {
flag := false
for {
if flag {
str = you
} else {
str = foo
}
flag = !flag
time.Sleep(5 * time.Millisecond)
}
}()

for {
if str == "bar" {
panic("bar")
}
fmt.Println(str)
time.Sleep(5 * time.Millisecond)
}
}

该代码运行一段时间后,会panic. 说明string 类型其实不是线程安全的。str内部结构包含len、data 两个字段,先修改了data,在没有修改len情况下,取值将取的是str的len长度的字符串,所以上述代码会panic。当然这也违背了Go语言:”不要通过共享内存来通信,而应该通过通信来共享内存”。