返回结构体或错误的惯用方式是什么?

huangapple go评论111阅读模式
英文:

What is the idiomatic way to return either a struct or an error?

问题

我有一个返回Card的函数,Card是一个struct类型,或者返回一个错误。

问题是,当发生错误时,我该如何从函数中返回?对于结构体来说,nil是无效的,而且我没有一个有效的零值来表示我的Card类型。

func canFail() (card Card, err error) {
    // return nil, errors.New("Not yet implemented"); // 失败
    return Card{Ace, Spades}, errors.New("not yet implemented"); // 可行,但很丑陋
}

我找到的唯一解决方法是使用*Card而不是Card,当发生错误时将其设置为nil,或者当没有错误发生时将其指向一个实际的Card,但这样做很笨拙。

func canFail() (card *Card, err error) {
    return nil, errors.New("not yet implemented");
}

有更好的方法吗?

编辑:我找到了另一种方法,但不知道这是否符合惯用法或良好的风格。

func canFail() (card Card, err error) {
    return card, errors.New("not yet implemented")
}

由于card是一个命名返回值,我可以在不初始化它的情况下使用它。它以自己的方式被置零,我并不真正关心,因为调用函数不应该使用这个值。

英文:

I have a function that returns either a Card, which is a struct type, or an error.

The problem is, how can I return from the function when an error occurs ? nil is not valid for structs and I don't have a valid zero value for my Card type.

func canFail() (card Card, err error) {
    // return nil, errors.New("Not yet implemented"); // Fails
    return Card{Ace, Spades}, errors.New("not yet implemented"); // Works, but very ugly
}

The only workaround I found is to use a *Card rather than a Card, a make it either nil when there is an error or make it point an actual Card when no error happens, but that's quite clumsy.

func canFail() (card *Card, err error) {
    return nil, errors.New("not yet implemented");
}

Is there a better way ?

EDIT : I found another way, but don't know if this is idiomatic or even good style.

func canFail() (card Card, err error) {
    return card, errors.New("not yet implemented")
}

Since card is a named return value, I can use it without initializing it. It is zeroed in its own way, I don't really care since the calling function is not supposed to use this value.

答案1

得分: 20

我认为,你的第三个例子也是可以的。通常的规则是,当一个函数返回一个错误时,除非文档明确说明,否则不能依赖其他返回值具有有意义的值。所以在这里返回一个可能没有意义的结构体值是可以的。

英文:
func canFail() (card Card, err error) {
    return card, errors.New("not yet implemented")
}

I think this, your third exampe, is fine too. The understood rule is that when a function returns an error, other return values cannot be relied upon to have meaningful values unless documentation clearly explains otherwise. So returning a perhaps meaningless struct value here is fine.

答案2

得分: 8

例如,

type Card struct {
}

func canFail() (card Card, err error) {
	return Card{}, errors.New("尚未实现")
}
英文:

For example,

type Card struct {
}

func canFail() (card Card, err error) {
	return Card{}, errors.New("not yet implemented")
}

答案3

得分: 3

func canFail() (card Card, err error) {
if somethingWrong {
err = errors.New("尚未实现")
return
}

        if foo {
                card = baz
                return
        }

        ... 

        // 或者 
        return Card{Ace, Spades}, nil
}
英文:
func canFail() (card Card, err error) {
        if somethingWrong {
                err = errors.New("Not yet implemented")
                return
        }

        if foo {
                card = baz
                return
        }

        ... 

        // or 
        return Card{Ace, Spades}, nil
}

答案4

得分: 1

对我来说,我更喜欢你的第二个选项。

func canFail() (card *Card, err error) {
    return nil, errors.New("not yet implemented");
}

这样,当发生错误时,canFail()的调用者无法使用card,因为它是nil。我们无法确保调用者会先检查错误。

英文:

For me, I prefer your second option.

func canFail() (card *Card, err error) {
    return nil, errors.New("not yet implemented");
}

This way you can make sure that when errors happen, the canFail() callers won't be able to use the card since it's nil. We can't make sure that the callers will check the error first.

答案5

得分: 1

peterSO的答案是最接近的,但不完全是我会使用的。我认为这是最好的:

func canFail() (Card, error) {
   return Card{}, errors.New("not yet implemented")
}

首先,它没有使用指针,只是为了可以使用nil作为返回值。我认为这是一个巧妙的技巧,但除非你实际上需要将struct作为指针(用于修改或其他原因),否则返回一个值更好。此外,我认为返回值不应该命名,除非你正在利用它们,就像这样:

func canFail() (card Card, err error) {
   return
}

这有两个问题。首先,你并不总是处于可以简单地让返回值成为那个变量的状态的情况下。其次,如果你有一个较大的函数,你将无法在更深层次使用裸返回,因为你会得到变量遮蔽错误。

最后,使用Card{}而不是nilcard更冗长,但它更好地传达了你正在做什么。如果你使用下面任何一个:

return
return card, err

在没有上下文的情况下,不清楚函数是否成功,而这个:

return Card{}, err

非常清楚函数失败了。这是你在使用基本类型时会使用的相同模式:

return false, err
return 0, err
return '\x00', err
return "", err
return []byte{}, err

https://github.com/golang/go/wiki/CodeReviewComments#pass-values

英文:

peterSO's answer is the closest, but it's not quite what I would use. I think this is best:

func canFail() (Card, error) {
   return Card{}, errors.New("not yet implemented")
}

First, it's not using a pointer just so it can use nil for returns. I think that's a neat trick, but unless you actually need the struct to be a pointer (for modifying or other reason), then returning a value is better. Also I don't think the return values should be named, unless you are utilizing them, like this:

func canFail() (card Card, err error) {
   return
}

and that is problematic for two reasons. First, you aren't always going to be in a situation where you can simply have the return value be whatever that variable is at the time. Second, if you have a larger function, you won't be able to use a naked return in the deeper levels, as you will get variable shadow errors.

Finally, using Card{} instead of nil or card is more verbose, but it better communicates what you are doing. If you use either of these:

return
return card, err

It's not clear without context if the function was successful or not, while this:

return Card{}, err

is pretty clear that the function failed. It's the same pattern you would use with primitive types:

return false, err
return 0, err
return '\x00', err
return "", err
return []byte{}, err

https://github.com/golang/go/wiki/CodeReviewComments#pass-values

答案6

得分: 0

作为返回结构体的可能替代方案,您可以考虑让调用者分配它并让函数设置参数。

func canFail(card *Card) (err error) {
	if someCondition {
		// 设置一个属性
		card.Suit = Diamond

		// 一次性设置所有属性
		*card = Card{Ace, Spade}
	} else {
		err = errors.New("something went wrong")
	}

	return
}

如果您不习惯假装Go支持C++风格的引用,您还应该检查card是否为nil

https://play.golang.org/p/o-2TYwWCTL

英文:

As a possible alternative to returning the struct you might consider letting the caller allocate it and the function set params.

func canFail(card *Card) (err error) {
	if someCondition {
		// set one property
		card.Suit = Diamond

		// set all at once
		*card = Card{Ace, Spade}
	} else {
		err = errors.New("something went wrong")
	}

	return
}

If you are not comfortable pretending that Go supports C++ style references you should also check card for being nil.

https://play.golang.org/p/o-2TYwWCTL

答案7

得分: 0

如果您的函数在其签名处的行为与其他人的假设不同,即“如果发生错误,我应该忽略其值”。

就像任何io.Reader一样,它可能返回n>0和一个error

那么,您应该简单地记录下来,向用户解释在处理返回值时应该考虑什么。

对于这种情况,虽然罕见但不可避免,改变签名,从而改变一般的API关系,并不是Go的方式。

相反,您应该适当地记录函数的行为。

英文:

If your function does not behave like someone else would assume reading at its signature, IE, if an error has occurred I should ignore the value along it.

Pretty much like any io.Reader, which may return n>0 with an error

Then, you should simply document it to explain to the user what should be considered regarding the returned value along the error.

Changing the signature, thus the general API relationships, for such case, rare but not unavoidable, is not the way to Go.

Instead, you should adequatly document the behavior of the function.

huangapple
  • 本文由 发表于 2013年3月11日 18:12:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/15335942.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定