在 Go 中使用命名返回变量捕获 panic
在下面代码中,如果pressButton发生panic,那么不会执行到return err,导致返回的err是nil。
func doStuff() error { var err error // If there is a panic we need to recover in a deferred func defer func() { if r := recover(); r != nil { err = errors.New("the meeseeks went crazy!") } }() pressButton() return err }
可以使用命名返回变量解决,即使我们从未触碰到 doStuff 函数的末尾的返回语句,也会立刻返回这个 err 变量。
func doStuff() (err error) { // If there is a panic we need to recover in a deferred func defer func() { if r := recover(); r != nil { err = errors.New("the meeseeks went crazy!") } }() pressButton() return err }
重新切片(slice)
从 slice 中重新切出新 slice 时,新 slice 会引用原 slice 的底层数组,将导致难以预料的内存使用。可以通过拷贝临时 slice 的数据,而不是重新切片来解决:
func get() (res []byte) { raw := make([]byte, 10000) fmt.Println(len(raw), cap(raw), &raw[0]) // 10000 10000 0xc420080000 res = make([]byte, 3) copy(res, raw[:3]) return } func test() { slice := a[2:3:4] //可以利用第三个参数控制容量 //如果设置容量和长度相同,可以append时新创建底层数组,不影响原有的 }
类型声明与方法
从一个现有的非 interface 类型创建新类型时,并不会继承原有的方法:
// 定义 Mutex 的自定义类型 type myMutex sync.Mutex func main() { var mtx myMutex mtx.Lock() mtx.UnLock() } //mtx.Lock undefined (type myMutex has no field or method Lock)…
如果你需要使用原类型的方法,可将原类型以匿名字段的形式嵌到你定义的新 struct 中:
// 类型以字段形式直接嵌入 type myLocker struct { sync.Mutex } func main() { var locker myLocker locker.Lock() locker.Unlock() }
for + 闭包函数
type field struct { name string } func (p *field) print() { fmt.Println(p.name) } // 错误示例 func main() { data := []field{ {"one"}, {"two"}, {"three"}} for _, v := range data { go v.print() } time.Sleep(3 * time.Second) // 输出 three three three } // 正确示例 func main() { data := []field{ {"one"}, {"two"}, {"three"}} for _, v := range data { v := v go v.print() } time.Sleep(3 * time.Second) // 输出 one two three } // 正确示例 func main() { data := []*field{ {"one"}, {"two"}, {"three"}} for _, v := range data { // 此时迭代值 v 是三个元素值的地址,每次 v 指向的值不同 go v.print() } time.Sleep(3 * time.Second) // 输出 one two three }
defer函数参数
对 defer 延迟执行的函数,它的参数会在声明时候就会求出具体值,而不是在执行时才求值:
// 在 defer 函数中参数会提前求值 func main() { var i = 1 defer fmt.Println("result: ", func() int { return i * 2 }()) i++ }
select default
如果有default子句,case不满足条件时执行该语句。
如果没有default字句,select将阻塞,直到某个case可以运行;Go不会重新对channel或值进行求值。
缓冲通道中,刚好相反,由于元素值的传递是异步的,所以发送操作在成功向通道发送元素值之后就会立即结束(它不会关心是否有接收操作)。
如果使用非缓冲,ret值必须被接收。如果此函数等待超时直接返回nil,会导致Ret这个Chan在模块那边写不进去堵死。
func (m *friendModule) Gift*****(cmd GiftCmd) *GiftRet { ctx, cancel := context.WithTimeout(context.Background(), util.ASyncCmdTimeOut) defer cancel() cmd.Ret = make(chan GiftRet, 1) ##非缓冲bug select { case m.GiftChan <- cmd: case <-ctx.Done(): logs.Error("friend moduel gift cmdChan is full") } select { case ret := <-cmd.Ret: return &ret case <-ctx.Done(): logs.Error("friend moduel gift cmdChan apply <-retChan timeout") return nil } }