注:本文的灵感来源于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