注:本文的灵感来源于GOPHER 2020年大会陈皓的分享,原PPT的链接可能并不方便获取,所以我下载了一份PDF到git仓,方便大家阅读。我将结合自己的实际项目经历,与大家一起细品这份文档。
目录
Functional
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| type Number struct { a int b string c bool d []int32 e error }
func (n *Number) parse(r io.Reader) error { if err := binary.Read(r, binary.BigEndian, &n.a); err != nil { return err } if err := binary.Read(r, binary.BigEndian, &n.b); err != nil { return err } if err := binary.Read(r, binary.BigEndian, &n.c); err != nil { return err } if err := binary.Read(r, binary.BigEndian, &n.d); err != nil { return err } if err := binary.Read(r, binary.BigEndian, &n.e); err != nil { return err } return nil }
|
引入了函数式编程的方式,我们看看有什么改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func (n *Number) parse(r io.Reader) error { var err error
read := func(data interface{}) { if err != nil { return } err = binary.Read(r, binary.BigEndian, data) }
read(&n.a) read(&n.b) read(&n.c) read(&n.d) read(&n.e)
return err }
|
ErrorObject
先看一个标准库中的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func main() { input := bytes.NewReader([]byte("hello")) scanner := bufio.NewScanner(input) for scanner.Scan() { token := scanner.Text() fmt.Println(token) } if err := scanner.Err(); err != nil { fmt.Println(err) } }
|
它的根本思想,是将error
嵌入到了对象中。那我们借鉴一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| type Reader struct { r io.Reader err error }
func (r *Reader) read(data interface{}) { if r.err == nil { r.err = binary.Read(r.r, binary.BigEndian, data) } }
func (n *Number) parse(reader io.Reader) error { r := Reader{r: reader}
r.read(&n.a) r.read(&n.b) r.read(&n.c) r.read(&n.d) r.read(&n.e)
return r.err }
|
捎带提一句:个人不太喜欢上面scanner
的错误处理方式,这个要求使用方对这个包很熟悉,否则很容易忘掉后面的错误处理逻辑。但后面处理错误的逻辑,就很直接地将错误返回,可读性很强。
Wrap
耗子叔给的例子是调用了github.com/pkg/errors
下的wrap包,不过我更倾向于直接用原生的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func main() { err := errors.New("level 1") fmt.Println(err)
wraped := fmt.Errorf("%v: %w", "level 2", err) fmt.Println(wraped)
unwraped := errors.Unwrap(wraped) fmt.Println(unwraped)
unwraped2 := errors.Unwrap(unwraped) fmt.Println(unwraped2) }
|
但在实际项目实践中,Wrap
的这个特性并不好用:
如何Wrap Error,在多人协同开发、多模块开发过程中,很难统一。而一旦不统一,容易出现示例中的过度Unwrap的情况。
所以,我认为与其花大精力在制定错误的标准上,还不如利用fmt.Errorf
将错误信息直观地表述出来。
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili:https://space.bilibili.com/293775192
公众号:golangcoding