我正在阅读The Go Programming Language一书及其对错误包和界面的描述
package errors
type error interface {
Error() string
}
func New(text string) error { return &errorString{text} }
type errorString struct { text string }
func (e *errorString) Error() string { return e.text }
Run Code Online (Sandbox Code Playgroud)
它说
errorString的基础类型是结构,而不是字符串,以保护其表示免受无意(或预谋)更新.
这是什么意思?包不会隐藏底层类型,因为errorString没有导出?
更新
这里是我使用实现测试代码errorString使用string来代替.请注意,当尝试从另一个包中使用它时,您不能只将字符串指定为错误.
package testerr
type Error interface {
Error() string
}
func New(text string) Error {
return errorString(text)
}
type errorString string
func (e errorString) Error() string { return string(e) }
Run Code Online (Sandbox Code Playgroud)
并使用建议的代码进行测试
func main() {
err := errors.New("foo")
err = "bar"
fmt.Prinln(err)
}
Run Code Online (Sandbox Code Playgroud)
编译时最终会产生错误
cannot use "bar" (type string) as type testerr.Error in assignment:
string does not implement testerr.Error (missing Error method)
当然,这有一个缺点,因为碰巧具有相同错误字符串的不同错误将评估为我们不想要的相等.
该书关于"保护代表免受无意更新"的解释对我来说似乎有误导性.无论errorString是结构还是字符串,错误消息仍然是一个字符串,字符串是不可变的规范.
这也不是关于独特性的争论.例如,errors.New("EOF") == io.EOF求值为false,尽管两个错误都具有完全相同的基础消息.即使errorString是一个字符串也是如此,只要errors.New返回一个指向它的指针(参见我的例子).
你可以说结构实现error是惯用的,因为这也是标准库引入自定义错误的方式.SyntaxError从encoding/json包中看一下:
type SyntaxError struct {
Offset int64 // error occurred after reading Offset bytes
// contains filtered or unexported fields
}
func (e *SyntaxError) Error() string { return e.msg }
Run Code Online (Sandbox Code Playgroud)
(来源)
此外,实现error接口的结构没有性能影响,并且不会在字符串实现上消耗更多内存.请参阅转到数据结构.
您的 testerr 包工作得很好,但它失去了“基于结构”的标准错误包的一个主要功能:不相等:
package main
import ( "fmt"; "testerr"; "errors" )
func main() {
a := testerr.New("foo")
b := testerr.New("foo")
fmt.Println(a == b) // true
c := errors.New("foo")
d := errors.New("foo")
fmt.Println(c == d) // false
}
Run Code Online (Sandbox Code Playgroud)
作为errorString普通字符串,具有相同字符串内容的不同错误变得相等。原始代码使用指向结构的指针,并且每个结构都分配一个新的结构,因此如果与相同的错误文本进行比较New,则返回的不同值是不同的。New==
不允许编译器在这里生成相同的指针。而“对 New 的不同调用会产生不同的错误值”这一特性对于防止意外的错误相等非常重要。*errorString您可以通过实现来修改您的测试人员以产生此属性Error。尝试一下:您需要一个临时地址来获取地址。它“感觉”不对。人们可以想象一个奇特的编译器,它将字符串值内部化,并可能返回相同的指针(因为它指向相同的内部化字符串),这将打破这个良好的不等式属性。