4 回答

TA贡献1797条经验 获得超4个赞
你可以使用这样的东西:
type errorChain struct {
err error
next *errorChain
}
func Wrap(errs ...error) error {
out := errorChain{err: errs[0]}
n := &out
for _, err := range errs[1:] {
n.next = &errorChain{err: err}
n = n.next
}
return out
}
func (c errorChain) Is(err error) bool {
return c.err == err
}
func (c errorChain) Unwrap() error {
if c.next != nil {
return c.next
}
return nil
}
https://go.dev/play/p/6oUGefSxhvF

TA贡献1833条经验 获得超4个赞
您将“很快”(Go 1.20?1.21?)能够返回错误的切片/树,而不是链接/包装。
(同时,mdobak/go-xerrors
是个不错的选择)
看
建议::errors
添加对包装多个错误的支持
背景
从 Go 1.13 开始,错误可以通过提供
Unwrap
返回包装错误的方法来包装另一个错误。
和函数对包装错误链errors.Is
进行errors.As
操作。一个常见的请求是寻求一种将错误列表组合成单个错误的方法。
提议
如果错误的类型具有方法,则错误会包装多个错误
Unwrap() []error重用名称
Unwrap
避免了与现有单数Unwrap
方法的歧义。
从中返回一个长度为 0 的列表Unwrap
意味着错误不会包含任何内容。调用方不得修改 返回的列表
Unwrap
。
返回的列表Unwrap
不得包含任何nil
错误。我们将术语“
error chain
”替换为“error tree
”。和函数已更新以解包多个错误
errors.Is
。errors.As
Is
如果树中的任何错误匹配,则报告匹配。
As
在树的中序先序遍历中找到第一个匹配错误。该
errors.Join
函数提供了一个简单的实现multierr
。
它不会压平错误。// Join returns an error that wraps the given errors. // Any nil error values are discarded. // The error formats as the text of the given errors, separated by newlines. // Join returns nil if errs contains no non-nil values. func Join(errs ...error) error该函数允许格式化动词
fmt.Errorf
的多个实例。%w
该
errors.Unwrap
函数不受影响:它在方法nil
出错时返回Unwrap() []error
。为什么这应该在标准库中?
该提案添加了标准库之外无法提供的内容:直接支持 和 中的错误
errors.Is
树errors.As
。现有的组合错误通过提供
Is
和As
方法来检查包含的错误,要求每个实现复制此逻辑,可能以不兼容的方式。
这最好在errors.Is
和中处理errors.As
,出于同样的原因,这些函数处理奇异展开。此外,该提案为生态系统提供了一种通用方法来表示组合错误,允许第三方实现之间的互操作。

TA贡献1795条经验 获得超7个赞
您的代码修改了包全局错误值,因此它本质上是错误的。这个缺陷与 Go 的错误处理机制无关。
根据您链接的文档,有两个错误处理助手:Is
, 和As
。Is
让你递归地解包一个错误,寻找一个特定的错误值,这必须是一个全局包才能有用。As
,另一方面,允许您递归地解包错误以查找给定类型的任何已包装错误值。
包装是如何工作的?您将错误 A 包装在一个新的错误值B中。Wrap()
帮助程序必须返回一个新值,就像fmt.Errorf
链接文档中的示例一样。Wrap
助手不应该修改被包装的错误的值。该值应被视为不可变的。事实上,在任何正常的实现中,该值都是 type error
,这样您就可以包装任何错误,而不是仅仅将自定义错误类型的同心值相互包装起来;并且,在这种情况下,您无权访问包装错误的字段来修改它们。本质上,Wrap
大致应该是:
func Wrap(err error) error { return &errGroup{err} }
就是这样。这不是很有用,因为您的实现errGroup
实际上并没有做任何事情——它不提供有关发生的错误的详细信息,它只是其他错误的容器。为了让它有价值,它应该有一条string
错误消息,或者像其他一些错误类型的方法IsNotFound
,或者比仅仅使用error
and更有用的东西fmt.Errorf
。
根据您的示例代码中的用法,您似乎还假设用例是说“我想将 A 包装在 C 中的 B”中,这是我在野外从未见过的,我想不出任何需要的场景。包装的目的是说“我收到了错误 A,我将把它包装在错误 B 中以添加上下文,然后返回它”。调用者可能将该错误包装在错误 C 中,依此类推,这就是递归包装的价值所在。
例如:https ://go.dev/play/p/XeoONx19dgX

TA贡献1780条经验 获得超5个赞
有几种方法,但有一件事你应该记住:如果你有多个错误,你可能需要将它作为一个错误片段来处理
例如,假设您需要检查所有错误是否相同,或者至少存在一个特定类型的错误,您可以使用下面的代码片段。
您可以扩展这个概念或使用一些现有的库来处理多重错误
type Errors []error
func (errs Errors) String() string {
…
}
func (errs Errors) Any(target error) bool{
for _, err := range errs {
if errors.Is(err,target) {
return true
}
}
return false
}
func (errs Errors) All(target error) bool{
if len(errs) == 0 { return false }
for _, err := range errs {
if !errors.Is(err,target) {
return false
}
}
return true
}
- 4 回答
- 0 关注
- 135 浏览
添加回答
举报