go语言处理 error

参考文章1

通过 匿名函数处理 error

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func parse(r io.Reader) (*Point, error) {
    var p Point
    var err error
    read := func(data interface{}) {
        if err != nil {
            return
        }
        err = binary.Read(r, binary.BigEndian, data)
    }

    read(&p.Longitude)
    read(&p.Latitude)
    read(&p.Distance)
    read(&p.ElevationGain)
    read(&p.ElevationLoss)

    if err != nil {
        return &p, err
    }
    return &p, nil
}

对比下面的方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func parse(r io.Reader) (*Point, error) {

    var p Point

    if err := binary.Read(r, binary.BigEndian, &p.Longitude); err != nil {
        return nil, err
    }
    if err := binary.Read(r, binary.BigEndian, &p.Latitude); err != nil {
        return nil, err
    }
    if err := binary.Read(r, binary.BigEndian, &p.Distance); err != nil {
        return nil, err
    }
    if err := binary.Read(r, binary.BigEndian, &p.ElevationGain); err != nil {
        return nil, err
    }
    if err := binary.Read(r, binary.BigEndian, &p.ElevationLoss); err != nil {
        return nil, err
    }
}

receiver 模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
scanner := bufio.NewScanner(strings.NewReader(input))
	// Set the split function for the scanning operation.
	scanner.Split(bufio.ScanWords)
	// Count the words.
	count := 0
	for scanner.Scan() {
		count++
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading input:", err)
	}

把 error 存起来,最后再调用 方法 获取到 error的值

sentinel error

预定义的特定错误, 我们叫为sentinel error, 这个名字来源于计算机编程中使用一个特定值来表示不可能进行进一步处理的做法。所以对于Go ,我们使用特定的值来表示错误。

1
2
3
4
if err == io.EOF {
    // 这里的 error 就和 io包 的 EOF 这个哨兵耦合了,用户必须要了解这个包,才能够使用
    return nil
}

error type

通过类型断音判断 是什么类型的异常

1
2
3
4
5
6
7
8
9
switch err := err.(type) {
		case nil:
		// call succeeded, nothing to do
		case *MyError:
			fmt.Println("error occurred on line:", err.Line)
   		default:
   		// unknown error
   		}
 

,但是error types共享error values许多相同的问题。因此,尽量避免自定义错误类型,或者至少避免将它们作为公共 API 的一部分。

不透明 error

种方式被称为不透明错误处理,只返回错误的值,而不假设其内容。 if err != nil { return err } 在某些情况下把错误简单分为发生错误和未发生错误是不够的。例如web开发中,我们可能需要判断连接超时(可能处理方式是隔一段时间重新发出rpc请求),还是远程服务器发生错误(可能处理方式是返回内容降级处理)。在这种情况下,我们可以断言错误实现了特定的错误行为,而不是值或者类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type temporary interface {
	Temporary() bool
}

// IsTemporary returns true if err is temporary
func IsTemporary(err error) bool {
	te, ok := err.(temporary)
	return ok && te.Temporary()
}
//应该尽量采用这种方式进行错误处理。

wrap errors

们通过不透明错误处理方式把错误传回到程序顶部,然后将错误打印到屏幕或者日志中。但是仅这样处理,我们得到的错误只有一个简单的错误原因,例如:“No such file or directory”。没有具体到哪个地方报错,使得我们找到代码出错的地方变得十分麻烦。

而且我们有时候会在方法上面打上各种各样的日志,我们可以通过 pkg/errors 这个包 的 Wrap 方法来包装 error的调用堆栈信息

go1.13之前)必要时,使用errors.Cause获取root error(原始错误),再进行和sentinel error判定。(go1.13及以后)errors包引入两个新函数,errors.Is和error.As,省去了Unwarp和Cause,使得代码实现更加方便。

1
2
3
4
if errors.Is(err, Errsomething) { ... }
if errors.As(err, &e) { ... }
// Errsomething是特定错误值,e是一个特定错误值的指针变量。
// 一个是判断是否为某个哨兵值,一个是判断是否为某种类型的异常,然后能否转型