首先,您链接的博客文章appError 不是 error。它是一个包装器,携带示例的实现使用的错误值和其他相关信息,它们没有被公开,也没有appError 也没有*appError用作error 值。
因此,您引用的示例与您的实际问题无关。但是要回答标题中的问题:
一般来说,一致性可能是原因。如果一个类型有很多方法并且有些需要指针接收器(例如因为它们修改了值),通常用指针接收器声明所有方法是有用的,所以类型的method sets和指针类型不会混淆。
关于error 实现的回答:当您使用struct 值来实现error 值时,使用非指针来实现error 接口是很危险的。为什么会这样?
因为error 是一个接口。接口值为comparable。并且通过比较它们包装的值来比较它们。 你会得到不同的比较结果,根据它们里面包含的值/类型!因为如果你在它们中存储指针,如果它们存储相同的指针,错误值将是相等的。如果你在其中存储非指针(结构),如果结构值相等,它们就相等。
详细说明并举例说明:
标准库有一个errors 包。您可以使用errors.New() 函数从string 值创建错误值。如果你看一下它的实现(errors/errors.go),很简单:
// Package errors implements functions to manipulate errors.
package errors
// New returns an error that formats as the given text.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
实现返回一个指向一个非常简单的结构值的指针。这样,如果您创建 2 个具有相同 string 值的错误值,它们将不相等:
e1 := errors.New("hey")
e2 := errors.New("hey")
fmt.Println(e1, e2, e1 == e2)
输出:
hey hey false
这是故意的。
现在如果你要返回一个非指针:
func New(text string) error {
return errorString{text}
}
type errorString struct {
s string
}
func (e errorString) Error() string {
return e.s
}
具有相同 string 的 2 个错误值将相等:
e1 = New("hey")
e2 = New("hey")
fmt.Println(e1, e2, e1 == e2)
输出:
hey hey true
尝试Go Playground 上的示例。
为什么这很重要的一个典型例子:查看存储在变量io.EOF 中的错误值:
var EOF = errors.New("EOF")
预计io.Reader 实现会返回此特定错误值以表示输入结束。因此,您可以和平地比较Reader.Read() 和io.EOF 返回的错误,以判断是否到达输入结束。您可以确定,如果它们偶尔返回自定义错误,它们将永远不会等于 io.EOF,这是 errors.New() 保证的(因为它返回指向未导出结构值的指针)。