golang和指针接收器中的自定义错误

Nat*_*n H 5 error-handling pointers pass-by-reference go

阅读有关Web和stackoverflow上的值接收器与指针接收器的信息,我了解以下基本规则:如果您不打算修改接收器,并且接收器相对较小,则不需要指针。

然后,阅读有关实现error接口的信息(例如https://blog.golang.org/error-handling-and-go),我看到该Error()函数的示例全部使用指针接收器。

但是,我们没有修改接收器,结构非常小。

我觉得没有指针(return &appError{}vs return appError{})的代码会更好。

示例使用指针有原因吗?

icz*_*cza 10

首先,你联系,把您的示例中的博客文章,appError不是一个error。它是一个包装器,用于承载错误值和示例实现所使用的其他相关信息,它们不会公开,appError*appError永远不会用作error值。

因此,您引用的示例与您的实际问题无关。但是要回答标题中的问题:

通常,一致性可能是原因。如果一个类型有很多方法并且有些需要指针接收器(例如,因为它们修改了值),那么通常使用指针接收器声明所有方法会很有用,因此不会混淆类型和指针类型的方法集

关于error实现的回答:当您使用struct值实现error值时,使用非指针实现error接口是很危险的。为什么会这样呢?

因为error是一个接口。并且接口值是可比的。通过比较它们包装的值来比较它们。您会根据其中包含的值/类型获得不同的比较结果!因为如果在其中存储指针,则如果它们存储相同的指针,则错误值将相等。并且,如果将非指针(结构)存储在它们中,则当结构值相等时它们也将相等。

要对此进行详细说明并显示一个示例:

标准库有一个errors软件包。您可以string使用errors.New()函数根据值创建错误值。如果看一下它的实现(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
}
Run Code Online (Sandbox Code Playgroud)

该实现返回一个指向非常简单的struct值的指针。这样,如果您创建两个具有相同string值的错误值,它们将不相等:

e1 := errors.New("hey")
e2 := errors.New("hey")
fmt.Println(e1, e2, e1 == e2)
Run Code Online (Sandbox Code Playgroud)

输出:

hey hey false
Run Code Online (Sandbox Code Playgroud)

这是故意的。

现在,如果您要返回非指针:

func New(text string) error {
    return errorString{text}
}

type errorString struct {
    s string
}

func (e errorString) Error() string {
    return e.s
}
Run Code Online (Sandbox Code Playgroud)

2个相同的错误值string将相等:

e1 = New("hey")
e2 = New("hey")
fmt.Println(e1, e2, e1 == e2)
Run Code Online (Sandbox Code Playgroud)

输出:

hey hey true
Run Code Online (Sandbox Code Playgroud)

Go Playground上尝试示例。

一个为什么如此重要的闪亮示例:查看存储在变量中的错误值io.EOF

var EOF = errors.New("EOF")
Run Code Online (Sandbox Code Playgroud)

期望io.Reader实现将这个特定的错误值返回给信号输入结束。因此,您可以和平比较返回的错误Reader.Read()io.EOF以判断是否到达输入结束。您可以确定,如果它们偶尔返回自定义错误,则它们将永远不会等于io.EOF,这是可以errors.New()保证的(因为它返回了指向未导出结构值的指针)。

  • @E.Pratt `error` 是一个接口类型,`&errorString{}` 是一个具体类型的值(`*errorString`),并且这个指针类型实现了 `error` 接口。返回此值时,将创建一个“error”接口值。任何类型实现了“error”的值都可以分配给“error”类型的变量,或者可以在返回类型为“error”时返回。 (3认同)
  • PG。GOPL(Donovan,Kernighan)的196也提出了同样的观点,但这个答案详细解释了这一点。在每种情况下返回指针或结构,New 创建一个不同的 errorString 实例。这是new返回的值,一个错误接口,这就是问题所在。当返回一个结构体时,错误接口的两个实例比较相等,因为它们具有相同的值,即使它们是从 2 个不同的 errorStruts 创建的。当指针被 ret'd 时,error 的两个 int 是不同的,因为指针引用 errorString 的两个单独的 inst(由每次调用 New 创建)。 (2认同)

dm0*_*514 1

不 :)

https://blog.golang.org/error-handling-and-go#TOC_2

Go 接口允许任何符合错误接口的内容由期望的代码处理error

type error interface {
    Error() string
}
Run Code Online (Sandbox Code Playgroud)

就像您提到的,如果您不打算修改状态,则几乎没有动力传递指针:

  • 分配到堆
  • 气相色谱压力
  • 可变状态和并发等

有趣的是,我个人认为看到这样的例子就是新的 Go 程序员默认青睐指针接收器的原因。