这是一个为Go语言添加错误 Package 的提案。
它是对Go2 error values(原始提案)的反提案。
请在这个问题中分享反馈、意见等。
在https://github.com/JavierZunzunegui/xerrors中找到提案的详细信息。
从整体上看,与原始提案相比,这个提案有以下特点:
- 对错误类型没有要求(没有
Unwrap() error
或等效项) - 以更强大的方式允许自定义错误转换为字符串
- 没有自动迁移到 Package 形式(代码不会立即使用 Package ,没有%w或等效项)
- 透明地将堆栈信息添加到 Package 的错误中
- 具有编译时安全实现和较少的陷阱
- 在不修改reflect.DeepEqual的情况下比较错误
关于这个提案背后的思想,有一个first iteration,但现在这被认为是过时的。
5条答案
按热度按时间qco9c6ql1#
没有对错误类型的要求(没有
Unwrap() error
或等效项)Unwrap
不是必需的;它是可选的。使用接口使我们能够对现有错误进行改造。在你的提案中,只有类型WrappingError
可以 Package 。现有的错误类型如os.PathError
如何支持Find
和FindTyped
?编译时安全实现,但有一些需要注意的地方
我们同意
FindTyped
比As
更安全,但我们已经拒绝了它,因为它更不通用,更笨拙(需要写两次类型)。现在我们有一个对As
的审查。可以在不修改 reflect.DeepEqual 的情况下比较错误
我不明白这一点。带有堆栈信息的错误将与当前提案面临相同的问题。没有堆栈信息的错误不会,也是如此。
实质性的区别似乎在于:
New
没有添加堆栈信息。正如我们在其他地方讨论过的那样,我们认为额外的信息是值得的。Similar
函数来比较错误,同时省略了堆栈跟踪。我们希望类似的东西能被添加到cmp
包中,这样它就可以在嵌套在其他值中的错误上工作。感谢你花时间改进和澄清你的提案。就我个人而言,我不认为这里有任何东西会让我重新考虑我们目前的提案。
yzuktlbb2#
Unwrap
不是必需的;它是可选的。使用接口允许我们对现有错误进行改造。在你的提案中,只有类型WrappingError
可以 Package 。现有的错误类型如os.PathError
如何支持Find
和FindTyped
呢?在这个提案中, Package 不是一个接口,而是一个具体的类型
WrappingError
。任何错误(如os.PathError
)都可以被 Package ,这意味着创建一个包含错误的WrappingError
(通过Wrap
或WrapWithOpts
)。Find
和FindTyped
是在一个WrappingError
中查找特定错误的方法,os.PathError
已经像其他错误一样得到支持。请参阅 https://github.com/JavierZunzunegui/xerrors/blob/master/find_test.go#L16此外,在原始提案中,虽然
Unwrap
不是编译器要求的,但为了实现所需的功能(As
、Is
、使用Printer
打印等),我认为它几乎是必需的。在这个提案中,所有错误需要实现的是Error() string
。可以在不修改 reflect.DeepEqual 的情况下比较错误。我不明白这一点。带有堆栈信息的错误将与当前提案中的错误具有相同的问题。没有堆栈信息的错误则不会,也是如此。
reflect.DeepEqual
在两个提案中都以相同的方式失败( Package 堆栈)。这个提案有一个Similar
,基本上是一个DeepEqual
,但忽略了堆栈,并且不需要从 Package 的错误中获取任何信息。在原始提案中无法做到这一点,因为根本问题是如何比较在所有方面都相同,除了它们 Package 了不同的错误之外的相同错误的错误,在这种情况下, Package 错误是包含 Package 错误的引用的接口,需要类似Compare(error) bool
的方法,但不能保证每个错误都会实现它并正确地执行。在这个提案中,只有一个错误具有这种属性(WrappingError
),并且正确地支持Similar
和Compare
。你的新提案没有添加堆栈信息。正如我们在其他地方讨论过的那样,我们认为额外的信息是值得的。
在这个提案中,错误只保留该错误的信息。堆栈信息 是一个错误,即
StackError
。来自New
(stringError
)的错误没有堆栈,但一旦被 Package 就会获得堆栈-Wrap(nil, New("whatever"))
结果中的WrappingError
具有一个StackError
和一个stringError
。再次说明,没有任何要求 任何 错误与堆栈或其他功能交互,它都是通过由WrappingError
通过Wrap
产生的stringError
抽象出来的。请参阅 https://github.com/JavierZunzunegui/xerrors/blob/master/stack_test.go#L19,展示了一个具有堆栈的 x36n35x。你添加了一个类似的函数来比较错误,同时省略了堆栈跟踪。我们希望如果有类似的东西被添加到 cmp 包中,这样就可以在嵌套在其他值中的错误上工作。
当然可以。在这里做起来会更容易一些,因为只有 x36n35x 有嵌套的错误。
b4wnujal3#
任何错误(如
os.PathError
)都可以被 Package ,不,我的意思是
os.PathError
如何将其当前 Package 的错误(在其Err
字段中)暴露给Find
等。不:除了它们具有不同的堆栈帧。而且并不是每个错误都需要实现某些功能,只要递归比较函数忽略帧即可。
ppcbkaq54#
不,我的意思是os.PathError如何将其当前 Package 的错误(在Err字段中)暴露给Find等函数。
我明白了。参考一下,PathError是
PathError
基本上是在做自己的 Package ,这不能与WrappingError
和这里提议的所有功能集成。需要两个更改:将它的
Error
方法从func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
改为func (e *PathError) Error() string { return e.Op + " " + e.Path }
。然后Wrap(err, &PathError{}).Error() {this proposal} == PathError{Err: err}.Error() {current go}
。另一个更改是完全删除
Err
字段,并通过xerrors.Find(err, ...)
访问它,例如超时等。事实上,os
已经具有IsTimeout
、IsPermission
等。到那时,一个由
xerrors.Wrap
生成的PathError
(实际上是一个WrapperError
类型)支持其现有功能和这里引入的新功能。当然这是一个破坏性的变化,但在此期间破坏的不是
PathError
而是新的 Package 功能。这与原始提案相同(或类似),直到错误得到Unwrap
和FormatError(p Printer) (next error)
,否则在这些更改下也无法正常工作。这个变化更大,确实如此,但那是因为在我的建议中,这种模式(包含另一个错误的错误)被认为是反模式,必须逐步淘汰,这需要更多的工作。rsl1atfo5#
不:除了它们有不同的堆栈帧。而且并不是每个错误都需要实现一些东西,只要递归比较函数忽略了堆栈帧。
这个问题仍然存在。或者说比较函数忽略了堆栈帧对我来说是一个警告——想要一个基于反射的方法来比较两个类型,根据字段的类型进行区分?这是“反射魔法”,在我看来,这是一个设计缺陷的体现。在标准库的其他地方有这样的方法吗?
此外,你关注于堆栈帧,但也许在后面,还有一些其他类似的属性需要添加。那么你就需要添加更多的反射魔法。在这个提案中,
StackError
没有什么特别之处,它只是简单地集成到了默认的Formatter
和Wrap
方法中,但如果不适合用户,他们完全可以修改它。