为什么errorString是一个结构,而不是一个字符串

时间:2015-12-17 12:53:06

标签: go

我正在阅读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 }

它说

  

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) }

使用建议的代码进行测试

func main() {
    err := errors.New("foo")
    err = "bar"
    fmt.Prinln(err)
}

编译时最终会产生错误

cannot use "bar" (type string) as type testerr.Error in assignment: string does not implement testerr.Error (missing Error method)

当然对此有一个缺点,因为碰巧有相同错误字符串的不同错误将评估为我们不想要的相等。

2 个答案:

答案 0 :(得分:5)

本书的解释"保护代表免受无意更新"看起来误导了我。无论errorString是结构还是字符串,错误消息仍然是字符串,字符串是immutable by specification

这也不是关于独特性的争论。例如,errors.New("EOF") == io.EOF计算结果为false,尽管两个错误都具有完全相同的基础消息。即使errorString是字符串,同样适用也是如此,只要errors.New返回指向它的指针(see my example。)

你可以说实现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 }

source

此外,实现error接口的结构体没有性能影响,并且不会在字符串实现上消耗更多内存。请参阅Go Data Structures

答案 1 :(得分:3)

您的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
}

errorString是一个普通字符串,具有相同字符串内容的不同错误变得相等。原始代码使用指向struct的指针,每个New分配一个新结构,因此如果与New进行比较,==返回的不同值是不同的,尽管是相同的错误文本。

此处不允许编译器生成相同的指针。 “对New的不同调用产生不同的错误值”的这一特性对于防止意外的错误相等非常重要。通过*errorString实现Error,可以修改您的测试人员以产生此属性。试一试:你需要一个临时的地址。它“感觉”错了。可以想象一个花哨的编译器内部化字符串值并可能返回相同的指针(因为它指向相同的内部化字符串),这将打破这个不错的不等式属性。