建议:Go 2:针对错误值的反提案(v2)

hjqgdpho  于 6个月前  发布在  Go
关注(0)|答案(5)|浏览(61)

这是一个为Go语言添加错误 Package 的提案。
它是对Go2 error values(原始提案)的反提案。
请在这个问题中分享反馈、意见等。
https://github.com/JavierZunzunegui/xerrors中找到提案的详细信息。
从整体上看,与原始提案相比,这个提案有以下特点:

  • 对错误类型没有要求(没有Unwrap() error或等效项)
  • 以更强大的方式允许自定义错误转换为字符串
  • 没有自动迁移到 Package 形式(代码不会立即使用 Package ,没有%w或等效项)
  • 透明地将堆栈信息添加到 Package 的错误中
  • 具有编译时安全实现和较少的陷阱
  • 在不修改reflect.DeepEqual的情况下比较错误

关于这个提案背后的思想,有一个first iteration,但现在这被认为是过时的。

qco9c6ql

qco9c6ql1#

没有对错误类型的要求(没有 Unwrap() error 或等效项)
Unwrap 不是必需的;它是可选的。使用接口使我们能够对现有错误进行改造。在你的提案中,只有类型 WrappingError 可以 Package 。现有的错误类型如 os.PathError 如何支持 FindFindTyped ?
编译时安全实现,但有一些需要注意的地方
我们同意 FindTypedAs 更安全,但我们已经拒绝了它,因为它更不通用,更笨拙(需要写两次类型)。现在我们有一个对 As 的审查。
可以在不修改 reflect.DeepEqual 的情况下比较错误
我不明白这一点。带有堆栈信息的错误将与当前提案面临相同的问题。没有堆栈信息的错误不会,也是如此。
实质性的区别似乎在于:

  • 你的 New 没有添加堆栈信息。正如我们在其他地方讨论过的那样,我们认为额外的信息是值得的。
  • 你添加了一个 Similar 函数来比较错误,同时省略了堆栈跟踪。我们希望类似的东西能被添加到 cmp 包中,这样它就可以在嵌套在其他值中的错误上工作。

感谢你花时间改进和澄清你的提案。就我个人而言,我不认为这里有任何东西会让我重新考虑我们目前的提案。

yzuktlbb

yzuktlbb2#

Unwrap 不是必需的;它是可选的。使用接口允许我们对现有错误进行改造。在你的提案中,只有类型 WrappingError 可以 Package 。现有的错误类型如 os.PathError 如何支持 FindFindTyped 呢?

在这个提案中, Package 不是一个接口,而是一个具体的类型 WrappingError。任何错误(如 os.PathError)都可以被 Package ,这意味着创建一个包含错误的 WrappingError(通过 WrapWrapWithOpts)。FindFindTyped 是在一个 WrappingError 中查找特定错误的方法,os.PathError 已经像其他错误一样得到支持。请参阅 https://github.com/JavierZunzunegui/xerrors/blob/master/find_test.go#L16

此外,在原始提案中,虽然 Unwrap 不是编译器要求的,但为了实现所需的功能(AsIs、使用 Printer 打印等),我认为它几乎是必需的。在这个提案中,所有错误需要实现的是 Error() string

可以在不修改 reflect.DeepEqual 的情况下比较错误。我不明白这一点。带有堆栈信息的错误将与当前提案中的错误具有相同的问题。没有堆栈信息的错误则不会,也是如此。

reflect.DeepEqual 在两个提案中都以相同的方式失败( Package 堆栈)。这个提案有一个 Similar,基本上是一个 DeepEqual,但忽略了堆栈,并且不需要从 Package 的错误中获取任何信息。在原始提案中无法做到这一点,因为根本问题是如何比较在所有方面都相同,除了它们 Package 了不同的错误之外的相同错误的错误,在这种情况下, Package 错误是包含 Package 错误的引用的接口,需要类似 Compare(error) bool 的方法,但不能保证每个错误都会实现它并正确地执行。在这个提案中,只有一个错误具有这种属性(WrappingError),并且正确地支持 SimilarCompare

你的新提案没有添加堆栈信息。正如我们在其他地方讨论过的那样,我们认为额外的信息是值得的。

在这个提案中,错误只保留该错误的信息。堆栈信息 是一个错误,即 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 有嵌套的错误。

b4wnujal

b4wnujal3#

任何错误(如 os.PathError )都可以被 Package ,
不,我的意思是 os.PathError 如何将其当前 Package 的错误(在其 Err 字段中)暴露给 Find 等。

  • 除了它们 Package 了不同的错误*,

不:除了它们具有不同的堆栈帧。而且并不是每个错误都需要实现某些功能,只要递归比较函数忽略帧即可。

ppcbkaq5

ppcbkaq54#

不,我的意思是os.PathError如何将其当前 Package 的错误(在Err字段中)暴露给Find等函数。
我明白了。参考一下,PathError是

type PathError struct {
        Op   string
        Path string
        Err  error
}

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 已经具有 IsTimeoutIsPermission 等。
到那时,一个由 xerrors.Wrap 生成的 PathError (实际上是一个 WrapperError 类型)支持其现有功能和这里引入的新功能。
当然这是一个破坏性的变化,但在此期间破坏的不是 PathError 而是新的 Package 功能。这与原始提案相同(或类似),直到错误得到 UnwrapFormatError(p Printer) (next error) ,否则在这些更改下也无法正常工作。这个变化更大,确实如此,但那是因为在我的建议中,这种模式(包含另一个错误的错误)被认为是反模式,必须逐步淘汰,这需要更多的工作。

rsl1atfo

rsl1atfo5#

  • 除了它们 Package 了不同的错误,*

不:除了它们有不同的堆栈帧。而且并不是每个错误都需要实现一些东西,只要递归比较函数忽略了堆栈帧。
这个问题仍然存在。或者说比较函数忽略了堆栈帧对我来说是一个警告——想要一个基于反射的方法来比较两个类型,根据字段的类型进行区分?这是“反射魔法”,在我看来,这是一个设计缺陷的体现。在标准库的其他地方有这样的方法吗?
此外,你关注于堆栈帧,但也许在后面,还有一些其他类似的属性需要添加。那么你就需要添加更多的反射魔法。在这个提案中,StackError 没有什么特别之处,它只是简单地集成到了默认的 FormatterWrap 方法中,但如果不适合用户,他们完全可以修改它。

相关问题