今週のGo
- defer FILO/panicになっても実行される
- recoverはpanicが起こる関数と同じgoroutineの中に置く
- deferと対になる処理について
- errorに判定を生やす標準ライブラリの実装例
ログにもありますが、試したGoのバージョンは1.7.3です
deferはFILOでpanicになっても実行される
package main import "fmt" func main() { defer func() { fmt.Println("1st in") }() defer func() { fmt.Println("2nd in") }() arr := []int{1, 2, 3} fmt.Printf("%v", arr[100]) // raise panic }
2nd in 1st in panic: runtime error: index out of range goroutine 1 [running]: panic(0x8efc0, 0xc42000a0d0) /usr/local/Cellar/go/1.7.3/libexec/src/runtime/panic.go:500 +0x1a1 main.main() /Users/yoshidanozomi/study/gopl/study/defer.go:10 +0x93 exit status 2
panicをrecoverで拾えるスコープはgoroutineの中
panicの情報をrecoverで拾える範囲がよくわかってなかったのですが、同じgoroutineで実行される範囲のようです。
package main import "fmt" func main() { defer func() { if err := recover(); err != nil { fmt.Println("err:", err) } }() panic("test") }
err: test
package main import "fmt" import "sync" func main() { var wg sync.WaitGroup wg.Add(1) go func () { defer wg.Done() // recoverはdeferの中でしか実行できない // 同じgoroutineの中であれば、 // recoverを書くのはpanicが起こる関数の中でなくてもよい defer func() { if err := recover(); err != nil { fmt.Println("err:", err) } }() recoverTest() }() wg.Wait() } func recoverTest() { panic("test") }
err: test
package main import "fmt" import "sync" func main() { var wg sync.WaitGroup wg.Add(1) // deferをpanicが起こるgoroutineの外に置いた場合 defer func() { if err := recover(); err != nil { fmt.Println("err:", err) } }() go func () { defer wg.Done() recoverTest() }() wg.Wait() } func recoverTest() { panic("test") }
$ go run defer.go panic: test goroutine 5 [running]: panic(0x89e00, 0xc42000a2c0) /usr/local/Cellar/go/1.7.3/libexec/src/runtime/panic.go:500 +0x1a1 main.recoverTest() /Users/woshidan/study/gopl/study/defer.go:28 +0x6d main.main.func2(0xc42000a2b0) /Users/woshidan/study/gopl/study/defer.go:21 +0x4a created by main.main /Users/woshidan/study/gopl/study/defer.go:22 +0x9a exit status 2
deferと対になる処理について
go fmt
で整形してくれるわけではないですが、Go Blog Defer, Panic, and Recoverあたりを見るに*1、
dst, err := os.Create(dstName) if err != nil { return } defer dst.Close()
のように、後始末が行われてほしい処理の近くにおくのがよいかもです。
errorに判定を生やす標準ライブラリの実装例
net/http ライブラリの実装例 :eyes: .
https://golang.org/src/net/net.go?h=Temporary#L375
// An Error represents a network error. type Error interface { error Timeout() bool // Is the error a timeout? Temporary() bool // Is the error temporary? }
Go、割と雑に一つのファイルに長く処理が書いてあったりして、復習してる間にhttpのコード眺めててひやっとしたり、いままで違う種類のプログラミングなので迷わしい。。なので慣れるまでもうちょっと読む量増やしてこ、ということで現場からは以上です。
*1:まあ、古いですがががが