年前拜读了Rob Pike的Errors are values,不禁感叹——原来错误可以这样处理。这里简单做一个笔记。
模式1:闭包记录异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var err error write := func(buf []byte) { if err != nil { return } _, err = w.Write(buf) } write(p0[a:b]) write(p1[c:d]) write(p2[e:f]) // and so on if err != nil { return err } |
这种方式适用于多次相同类型调用的错误处理。缺点就是,每个类似的地方都需要引入一个闭包函数,显得冗余重复。
模式2:组合error struct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
type errWriter struct { w io.Writer err error } func (ew *errWriter) write(buf []byte) { if ew.err != nil { return } _, ew.err = ew.w.Write(buf) } ... ew := &errWriter{w: fd} ew.write(p0[a:b]) ew.write(p1[c:d]) ew.write(p2[e:f]) // and so on if ew.err != nil { return ew.err } |
代码比第一种方式更加干净。但是与第一种方式有一样的问题,就是无法具体的知道是在哪个位置发生的错误以及错误的具体信息。
模式3:error转化为panic
来自知乎唐生的回答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func ce(msg string, err error) { if err != nil { panic(fmt.Errorf("%s error: %v", msg, err)) } } func catch(err *error) { if p := recover(); p != nil { *err = p.(error) } } func foo() (err error) { defer catch(&err) // ... ce("duang", err) // ... ce("duang", err) // ... ce("duang", err) return } |
这种方式的通用性较好。但是把error主动转化为panic会导致goroutine的stack unwind。这会带来一定的系统开销,甚至是系统性能抖动。
参考: