从Panic中恢复
recover()的调用仅当它在defer函数中被直接调用时才有效。在defer中和直接调用这两个是必要条件
在Slice, Array, and Map "range"语句中更新引用元素的值
在“range”语句中生成的数据的值是真实集合元素的拷贝。它们不是原有元素的引用。这意味着更新这些值将不会修改原来的数据。同时也意味着使用这些值的地址将不会得到原有数据的指针。
如果你需要更新原有集合中的数据,使用索引操作符来获得数据。
package main
import "fmt"
func main() {
data := []int{1, 2, 3}
for i, _ := range data {
data[i] *= 10
}
fmt.Println("data:", data) //prints data: [10 20 30]
}
如果你的集合保存的是指针,那规则会稍有不同。如果要更新原有记录指向的数据,你依然需要使用索引操作,但你可以使用for range语句中的第二个值来更新存储在目标位置的数据。
package main
import "fmt"
func main() {
data := []*struct{ num int }{{1}, {2}, {3}}
for _, v := range data {
v.num *= 10
}
fmt.Println(data[0], data[1], data[2]) //prints &{10} &{20} &{30}
}
slice中的隐藏数据
因为slice是一个指针引用,所以如果引用原有数据的一小部分,实际指向的还是原来的整个内存对象。
对slice截断对象的append操作
因为slice很像指针,整个操作很容易产生“越界覆盖”
类型声明和方法
当你通过把一个现有(非interface)的类型定义为一个新的类型时,新的类型不会继承现有类型的方法。
Fails:
package main
import "sync"
type myMutex sync.Mutex
func main() {
var mtx myMutex
mtx.Lock() //error
mtx.Unlock() //error
}
如果你确实需要原有类型的方法,你可以定义一个新的struct类型,用匿名方式把原有类型嵌入其中。
package main
import "sync"
type myLocker struct {
sync.Mutex
}
func main() {
var lock myLocker
lock.Lock() //ok
lock.Unlock() //ok
}
defer的函数什么时候执行?
被defer的调用会在包含的函数的末尾执行,而不是包含代码块的末尾。对于Go新手而言,一个很常犯的错误就是无法区分被defer的代码执行规则和变量作用规则。如果你有一个长时运行的函数,而函数内有一个for循环试图在每次迭代时都defer资源清理调用,那就会出现问题。
解决办法就是将相关的逻辑封装成内部匿名函数,这样在函数结束时,就会调用defer指定的函数
更新map的值
如果你有一个struct值的map,你无法更新单个的struct值。这个操作无效是因为map元素是无法取址的。
但是slice元素是可以取址的。
栈和堆变量
你并不总是知道变量是分配到栈还是堆上。在C++中,使用new创建的变量总是在堆上。在Go中,即使是使用new()或者make()函数来分配,变量的位置还是由编译器决定。编译器根据变量的大小和“泄露分析”的结果来决定其位置。这也意味着在局部变量上返回引用是没问题的,而这在C或者C++这样的语言中是不行的。
如果你想知道变量分配的位置,在“go build”或“go run”上传入“-m“ gc标志(即,go run -gcflags -m app.go)。
多goroutine的读写顺序可能被重排
多个goroutine运行时,顺序的语句执行次序可能会发生调换。