Goã®ã¨ã©ã¼å¦çâ¡ãpanicã¨recover
Goã®panicã¨recover
Goã®ã¨ã©ã¼å¦çã¯ååã¨ãã¦ä¾å¤ã使ãããã¨ã©ã¼ãä¸å¯§ã«å¦çãã¹ãæ¹éã¯ããã£ãã¨ãã¦ãGoã®ä¾å¤æ©æ§ã¨ãããã®ãã©ãããä»çµãªã®ã調ã¹ãã
ã¾ããåºæ¬ã«å¿ å®ã«ãgolang.jpã®èª¬æãèªãã¨ã
Recover
panicãå¼ã³åºãããã¨ã(ããã«ã¯ãé åã®ã¤ã³ããã¯ã¹ãç¯å²å¤ã§ããã¨ãããåã¢ãµã¼ã·ã§ã³ã«å¤±æãããããªæé»çãªãã®ãå«ã)ã¯ããããã¾ãã«ã¬ã³ãã®é¢æ°ã忢ããã´ã«ã¼ãã³ã®ã¹ã¿ãã¯ã®å·»ãæ»ããéå§ãã¾ãããã®éä¸ãé å»¶æå®ããã¦ãã颿°ããã¹ã¦å®è¡ãã¾ãããã®å·»ãæ»ããã´ã«ã¼ãã³ã®ã¹ã¿ãã¯ã®å é ã«ãã©ãçãã¨ãããã°ã©ã ã¯çµäºãã¾ãããã ããçµã¿è¾¼ã¿é¢æ°recoverã使ããã¨ã§ãã´ã«ã¼ãã³ã®å¶å¾¡ãåãæ»ããé常ã®å®è¡ãåéããããã¨ãå¯è½ã§ãã
panicãçºçããã¨å³åº§ã«å¦çãæã¡åãã®ã§ããããä»ã®è¨èªã®ä¾å¤ã«ç¸å½ãããã®ãããã
ããã®éä¸ãé å»¶æå®ããã¦ãã颿°ããã¹ã¦å®è¡ãã¾ããã¨ãè¨ã£ã¦ããã®ã§ãdeferã«ç¡å颿°ãæå®ãã¦recoverããªãã¨ãã¡ãªããããªããã¨æã£ããããä¸å¯§ã«ãã®å¾ã«æè¨ããã¦ããã
recoverãå¼ã³åºãã¨ãå·»ãæ»ãã忢ããpanicã«æ¸¡ããã弿°ãè¿ãã¾ããå·»ãæ»ãä¸ã«å®è¡ã§ããã³ã¼ãã¯ãdefferã§é å»¶æå®ããã颿°å ã®ã¿ãªã®ã§ãrecoverã¯é å»¶æå®ããã颿°å ã§ã®ã¿å½¹ç«ã¡ã¾ãã
Pythonã¨ã®æ¯è¼
ä¾ãã°ããªãã©ã¤å¯è½ãªä¾å¤ããµãã«ã¼ãã³ããéåºãããå ´åã¯ä¾å¤ãæ¡ãã¤ã¶ãããã以å¤ã¯ä¸ä½ã«ã¼ãã³ã«éåºããããã°ã©ã ãPythonã§æ¸ãã¨ãããªæãã ããã
class Retryable(Exception): pass try: do_something() // ä½ãä¾å¤ãéåºããå¯è½æ§ãããå¦ç except Retryable as ex: print(ex) except Exception as ex: raise ex
æ®éã«try-exceptãããã¯ä½¿ãã§ãããã
対ãã¦ãGoã§ä¼¼ããããªãã¨ããããã¨ããã¨ããããªæãã ããã
type Retryable struct{} defer func() { switch p := recover(); p { case nil: // ããã¯ãããã¯ãèµ·ããªãã£ãå ´åã該å½ãã fmt.Println("No error.") case Retryable{}: err = fmt.Errorf("failed, retry") // err = errors.New("failed, retry") default: panic(p) } }()
Pythonããããã¯ãæ±ãæ¹æ³ãæ¡ãã®ã«å¯¾ããGoã¯ãããã¯ãç£è¦ããä»çµã¿ã§ã¯ãªãããã¾ã§ã颿°ãã¼ã¹ã®è§£æ±ºæ¹æ³ãæ¡ããã¨ãããåããã
ãå®è·µGoè¨èªãã®ãã¼ã¸ã«ã¯ãã®ããã«ãæ¸ãã¦ããã
ä»è¨èªã®ãããã¯ã¬ãã«ã®ãªã½ã¼ã¹ç®¡çã«æ £ããããã°ã©ãã«ã¯ãdeferã¯ç¹ç°ã«è¦ããããç¥ãã¾ããããdeferã®ãã£ã¨ãé¢ç½ããå¼·åãªæ´»ç¨æ³ã¯ãdeferããããã¯ãã¼ã¹ã§ã¯ãªãã颿°ãã¼ã¹ã§ãããã¨ã«ãã£ã¦çã¿åºããã¾ããpanicã¨recoverã®ã»ã¯ã·ã§ã³ã«ããã®å¯è½æ§ã示ãä¾ãããã¾ãã
ãã£ã±ããrecoverã¯deferã¨ä¸ç·ã«ä½¿ããªãã¨æå³ããªãã¿ããã ã
ã¾ããPythonã®ã³ã³ããã¹ãããã¼ã¸ã£ã¯ã¾ãã«ãããã¯ãç£è¦ããä»çµã¿ãªã®ã§ãGoã§åããã¨ããããã¨æã£ããdeferã使ãã®ã ããã
ã¨ã¯ãããPythonã§ã³ã³ããã¹ãããã¼ã¸ã£ãä½ãã¨ãã¯contextlib.contextmanagerã§é¢æ°ãã©ããããã°ç°¡åã«ä½ããã®ã§ãGoã®æµåã§Pythonã®ã³ã³ããã¹ãããã¼ã¸ã£ãèããã¨ãPythonã«ã¯é¢æ°ãã¼ã¹ã®åºæ¬çãªã¢ããã¼ãã®ä¸ã«ããããã¯ãã¼ã¹ã®ä»çµã¿ã«ç½®ãæããæ¹æ³ãç¨æããã¦ããã¨æããã¹ãã(è¬)ã
contextlibã使ãå ´åã¯ãããªæãã
from contextlib import contextmanager class Retryable(Exception): pass @contextmanager def retrying(): try: yield except Retryable as ex: print(ex) # except Exception as ex: # raise ex with retrying(): do_something() // ä½ãä¾å¤ãéåºããå¯è½æ§ãããå¦ç
å®éã«è©¦ãã
ãã£ãããªã®ã§ãã¨ã©ã¼ãè¿å´ããååçãªããæ¹ã¨ãpanic/recoverãå©ç¨ããæ¹æ³ããããã使ãåãããµã³ãã«ã³ã¼ããæ¸ãã¦ã¿ã(以ä¸)ã
package main import "fmt" type Challenger struct { count int threshold int } type Retryable struct{} // éå ¬éã¡ã½ããã®æ¹ã¯ããã¨panicãå©ç¨ããã func (c *Challenger) attack() bool { if c.count < c.threshold { c.count++ panic(Retryable{}) // ãããã¯ãããããªãã©ã¤å¯è½ } if c.count > c.threshold { panic("No more chance.") } c.count++ return true } // å ¬éã¡ã½ããã¯panicãææãã¦ãªã«ãã¼ããããã func (c *Challenger) Attack() (ok bool, err error) { defer func() { switch p := recover(); p { case nil: // ããã¯ãããã¯ãèµ·ããªãã£ãå ´åã該å½ãã fmt.Println("No error.") case Retryable{}: err = fmt.Errorf("Failed(%v times)", c.count) default: panic(p) } }() ok = c.attack() return } func main() { var c *Challenger c = &Challenger{0, 3} fmt.Println(c.Attack()) // false Failed(1 times) fmt.Println(c.Attack()) // false Failed(2 times) fmt.Println(c.Attack()) // false Failed(3 times) fmt.Println(c.Attack()) // true <nil> fmt.Println(c.Attack()) // 以ä¸panic // panic: No more chance. [recovered] // panic: No more chance. // // goroutine 1 [running]: // panic(0x4b9f40, 0xc82000a400) // /usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6 // main.(*Challenger).Attack.func1(0xc820039e30, 0xc820039e58) // /home/echizen/_go/src/github.com/oyakata/tokyo/trash/retry.go:35 +0x2fc // panic(0x4b9f40, 0xc82000a400) // /usr/lib/go-1.6/src/runtime/panic.go:443 +0x4e9 // main.(*Challenger).attack(0xc820039e58, 0x52c678) // /home/echizen/_go/src/github.com/oyakata/tokyo/trash/retry.go:19 +0xd0 // main.(*Challenger).Attack(0xc820039e58, 0x0, 0x0, 0x0) // /home/echizen/_go/src/github.com/oyakata/tokyo/trash/retry.go:38 +0x78 // main.main() // /home/echizen/_go/src/github.com/oyakata/tokyo/trash/retry.go:49 +0x52c // exit status 2 }