golang和指针接收器中的自定义错误 [英] Custom errors in golang and pointer receivers

查看:110
本文介绍了golang和指针接收器中的自定义错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

我觉得没有指针(return &appError{}return appError{})的代码要好得多.

示例使用指针有理由吗?

解决方案

首先,您链接并作为示例的博客文章appError不是不是,而是error.这是一个包装程序,错误地携带一个错误值以及示例的实现所使用的其他相关信息,它们没有公开,并且appError*appError都不存在用作error值.

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

通常,一致性可能是原因.如果一个类型有很多方法并且有些需要指针接收器(例如,因为它们修改了值),通常使用指针接收器声明所有方法很有用,因此对于 error 是一个接口.接口值是可比的.通过比较它们包装的值来比较它们. 然后您将得到不同的比较结果,这是基于其中包装了哪些值/类型!因为如果在其中存储指针,则如果它们存储相同的指针,则错误值将相等.而且,如果将非指针(结构)存储在它们中,则在结构值相等的情况下它们也是相等的.

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

标准库具有 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
}

该实现返回一个指向非常简单的struct值的指针.这样,如果您创建两个具有相同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

转到游乐场上尝试示例.

一个为什么如此重要的闪亮示例:查看存储在变量 :

var EOF = errors.New("EOF")

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

Reading about value receivers vs pointer receivers across the web and stackoverflow, I understand the basic rule to be: If you don't plan to modify the receiver, and the receiver is relatively small, there is no need for pointers.

Then, reading about implementing the error interface (eg. https://blog.golang.org/error-handling-and-go), I see that examples of the Error() function all use pointer receiver.

Yet, we are not modifying the receiver, and the struct is very small.

I feel like the code is much nicer without pointers (return &appError{} vs return appError{}).

Is there a reason why the examples are using pointers?

解决方案

First, the blog post you linked and took your example from, appError is not an error. It's a wrapper that carriers an error value and other related info used by the implementation of the examples, they are not exposed, and not appError nor *appError is ever used as an error value.

So the example you quoted has nothing to do with your actual question. But to answer the question in title:

In general, consistency may be the reason. If a type has many methods and some need pointer receiver (e.g. because they modify the value), often it's useful to declare all methods with pointer receiver, so there's no confusion about the method sets of the type and the pointer type.

Answering regarding error implementations: when you use a struct value to implement an error value, it's dangerous to use a non-pointer to implement the error interface. Why is it so?

Because error is an interface. And interface values are comparable. And they are compared by comparing the values they wrap. And you get different comparison result based what values / types are wrapped inside them! Because if you store pointers in them, the error values will be equal if they store the same pointer. And if you store non-pointers (structs) in them, they are equal if the struct values are equal.

To elaborate on this and show an example:

The standard library has an errors package. You can create error values from string values using the errors.New() function. If you look at its implementation (errors/errors.go), it's simple:

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

The implementation returns a pointer to a very simple struct value. This is so that if you create 2 error values with the same string value, they won't be equal:

e1 := errors.New("hey")
e2 := errors.New("hey")
fmt.Println(e1, e2, e1 == e2)

Output:

hey hey false

This is intentional.

Now if you would return a non-pointer:

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

type errorString struct {
    s string
}

func (e errorString) Error() string {
    return e.s
}

2 error values with the same string would be equal:

e1 = New("hey")
e2 = New("hey")
fmt.Println(e1, e2, e1 == e2)

Output:

hey hey true

Try the examples on the Go Playground.

A shining example why this is important: Look at the error value stored in the variable io.EOF:

var EOF = errors.New("EOF")

It is expected that io.Reader implementations return this specific error value to signal end of input. So you can peacefully compare the error returned by Reader.Read() to io.EOF to tell if end of input is reached. You can be sure that if they occasionally return custom errors, they will never be equal to io.EOF, this is what errors.New() guarantees (because it returns a pointer to an unexported struct value).

这篇关于golang和指针接收器中的自定义错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆