woshidan's loose leaf

ぼんやり勉強しています

今週の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:まあ、古いですがががが