웃기네요... Go 오류 처리의 다른 아기가 문제를 해결할 수 있습니까?

안녕하세요 여러분 생선튀김입니다.

Go 프로그래밍에서 오류 처리 메커니즘의 처리는 항상 논의됩니다. 하지만 Go1은 크게 움직일 수 없으니 계속해서 최적화할 수 있는 방법을 찾아보자.

오늘은 5월 연휴에 공부하다가 본 새로운 제안을 생선튀김에서 소개합니다.

배경

이 단계에서 표준 라이브러리에서 버그를 래핑할 수 있는 유일한 방법은 를 사용하는 fmt.Errorf것이며 조작할 공간은 상대적으로 작습니다.

이것은 오류에 대해 우리가 할 수 있는 모든 것은 오류의 내용을 .Error()출력 .

다음 코드:

	err := fmt.Errorf("煎鱼:%s", errors.New("放假中"))
	if err != nil {}
复制代码

그러나 비즈니스 요구 사항은 종종 그렇게 간단하지 않습니다. 이때 에러 메시지를 받았을 때 스택으로 돌아가 다른 정보(예: 비즈니스 상태 코드)를 제공하고 싶다면 특별히 간단한 방법은 없다.

다음과 같이 3가지 옵션만 있습니다.

  • 새로운 다른 오류를 반환할 수 있지만 원래 오류의 컨텍스트 정보는 손실됩니다.
  • fmt.Errorf오류 를 래핑하는 데 사용할 수 있습니다 . 이것은 호출자가 프로그래밍 방식으로 확인할 수 있는 것이 아니라 텍스트 출력에 추가할 뿐입니다.
  • error.Is작업 을 확인 하고.As 호출자가 오류의 근본 원인에 액세스할 수 있도록 하려는 메타데이터가 포함된 복잡한 오류 래퍼 구조를 작성할 수 있습니다 ..Unwrap

이제 가장 신뢰할 수 있는 방법은 세 번째 방법으로 가장 완성도가 높으며 Go1.13에 추가된 오류 계열 방법에 해당하며 아직 중년 단계(많은 사람들에게 추가된 유일한 새로운 오류 처리 완료)입니다. 연령).

제안서의 원저자는 이 단계에서 여전히 간단하고 편리하지 않다고 생각합니다.

새로운 제안

새로운 제안은 표준 라이브러리 오류에서 더 간단한 기능을 구현하여 위의 3번 항목의 효과를 달성하는 것입니다. 모든 오류를 다른 오류로 래핑하여 새로운 래핑된 오류 목록을 형성하도록 지원합니다.

다음 코드:

// With returns an error that wraps err with other.  
func With(err, other error) error
复制代码

이 래핑된 오류는 연결 목록과 유사하며 목록 errors.Unwrap을 . 연결 리스트 저장의 경우 순서에 문제가 있습니다.

With 函数中,other 参数的错误将会放在包装错误列表的头部。如果在调用 With 函数时是 With(b->a, d->c),呈现在内的错误列表是:d->c->b->a。

对应的使用场景:

  • errors.Is(errors.With(err, other)):
    • 判别标准:errors.Is(other) || errors.Is(err)。
  • errors.As(errors.With(err, other), target):
    • 判别标准:errors.As(other, target) || errors.As(err, target)
  • errors.With(err, other).Error():
    • 输出结果是 other.Error() + ": " + err.Error()。

提案作者@Nate Finch 希望通过这种错误包装方式,对既有的代码改动是最小的。也能提供最广泛的功能适用性,认为是有价值的。

案例

场景

作者给出了一个非常经典的用户案例。在我们平时写应用代码时,在写过的每个 go 应用程序中都看到了它。

应用中有一个返回特定域错误的包,例如返回 pq.ErrNoRows 的 postgres 驱动程序。

您希望将该错误向上传递到堆栈以维护原始错误的上下文,但您不希望调用者必须知道 postgres 错误才能知道如何从存储层处理此错误。

改造

可以使用新的 With 函数,您可以通过众所周知的错误类型添加元数据,以便可以一致地检查您的函数返回的错误,而不管底层实现如何。

如下代码:

// SetUserName sets the name of the user with the given id. This method returns 
// flags.NotFound if the user isn't found or flags.Conflict if a user with that
// name already exists. 
func (st *Storage) SetUserName(id uuid.UUID, name string) error {
    err := st.db.SetUser(id, "name="+name)
    if errors.Is(err, pq.ErrNoRows) {
       return nil, errors.With(err, flags.NotFound)
    }
    var pqErr *pq.Error
    if errors.As(err, &pqErr) && pqErr.Constraint == "unique_user_name" {
        return errors.With(err, flags.Conflict)
    }
    if err != nil {
       // some other unknown error
       return fmt.Errorf("error setting name on user with id %v: %w", err) 
    }
    return nil
}
复制代码

这种错误通常称为哨兵错误。

总结

今天给大家介绍的这个提案,还是比较贴合我们日常工作中的使用场景的。平时写 Go 应用程序,思考的多,就会折腾这个问题。会出现,莫非要根据错误文本来判断错误内容?

因此像是业内错误库,或是之前看毛老师讲的,都会进行相关的设计。这份提案也是一个不错的补充了。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo… 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读

参考

추천

출처juejin.im/post/7101159289053511710