预期为nil,但返回了一个具有nil值的接口,该接口应该是nil。

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

Expecting nil but getting an interface with a nil value in return, which should be nil

问题

运行它在
https://play.golang.org/p/sl12vfS9vP

package main

import "fmt"

func main() {
    err := run()
    if err != nil {
        fmt.Printf("%#v", err)
    }
}

func run() (err error) {
    return check()
}

func check() *Result {
    return nil
}

type Result struct {
    message string
}

func (result *Result) Error() string {
    return result.message
}
英文:

Run it at
https://play.golang.org/p/sl12vfS9vP

package main

import "fmt"

func main() {
	err := run()
	if err != nil {
		fmt.Printf("%#v", err)
	}
}

func run() (err error) {
	return check()
}

func check() *Result {
	return nil
}

type Result struct {
	message string
}

func (result *Result) Error() string {
	return result.message
}

答案1

得分: 4

这在常见问题解答Go陷阱网站上有讨论:

只有当内部值和类型都未设置(nil, nil)时,接口值才为nil。特别地,nil接口将始终持有一个nil类型。如果我们将类型为int的指针存储在接口值中,无论指针的值如何,内部类型都将是int:(*int,nil)。因此,即使内部指针为nil,这样的接口值也将是非nil的。

(...)

为了向调用者返回一个正确的nil错误,函数必须显式返回nil:

func returnsError() error {
    if bad() {
        return ErrBad
    }
    return nil
}
英文:

This is discussed on the FAQ and the Go Traps website:

> An interface value is nil only if the inner value and type are both unset, (nil, nil). In particular, a nil interface will always hold a nil type. If we store a pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (*int, nil). Such an interface value will therefore be non-nil even when the pointer inside is nil.

>(...)

>To return a proper nil error to the caller, the function must return an explicit nil:
>
func returnsError() error {
if bad() {
return ErrBad
}
return nil
}

答案2

得分: 2

Francesc Campoy Flores(来自Google的Go团队)在今年的dotGo大会上谈到了这个特定的问题。

你可以将接口值看作有两个部分:类型和值。因此,类型为*Result且值为nil的接口与类型和值都为nil的接口是不相等的。

你可以通过在比较中指定nil来修复你的代码:

package main

import "fmt"

func main() {
    err := run()
    if err != (*Result)(nil) {
        fmt.Printf("%#v", err)
    }
}

func run() (err error) {
    return check()
}

func check() *Result {
    return nil
}

type Result struct {
    message string
}

func (result *Result) Error() string {
    return result.message
}
英文:

Francesc Campoy Flores (from the Go team at Google) talked about this particular issue in his presentation at dotGo this year.

You can think an interface value has 2 parts; a type and a value. Therefore an interface of type *Result and value nil is not equal to an interface with both type and value nil.

You can fix your code by typing the nil in the comparison:

http://play.golang.org/p/E9kro7Fkbr

package main

import "fmt"

func main() {
	err := run()
	if err != (*Result)(nil) {
		fmt.Printf("%#v", err)
	}
}

func run() (err error) {
	return check()
}

func check() *Result {
	return nil
}

type Result struct {
	message string
}

func (result *Result) Error() string {
	return result.message
}

答案3

得分: 0

简而言之:在使用直接返回值或命名返回值的函数中,不要混合使用接口和指针/值。

目标是创建一个错误聚合器,例如:

func checkStatus() *Result {
    r := &Result{}
    // ... 进行检查并将结果附加到 r.message ...
    if r.message == "" {
        return nil
    }
    return r
}

func checkSomething() error {
    return runDiagnostics()
}

func runDiagnostics() *Result {
    ... 做一些操作
    // 一切正常
    return nil
}

.. 其他地方 .... (使用命名返回值)
if err = checkStatus(); err != nil {
    return
}

.. 即使 runDiagnostics() 返回 nil这里仍然失败 ...
if err = checkSomething(); err != nil {
    ... err 总是 "something"
}

使用指针进行详细检查:http://play.golang.org/p/WPGy_ooXmP

使用接口进行详细检查:http://play.golang.org/p/JjWxEj9WRX

使用错误指示器的解决方案:http://play.golang.org/p/C30s49GiIN

package main

import "fmt"

func main() {
    err := run()
    if err != nil {
        fmt.Printf("%#v", err)
    }
}

func run() (err error) {
    _, err = check()
    return
}

func check() (*Result, error) {
    return nil, nil
}

type Result struct {
    message string
}

func (result *Result) Error() string {
    return result.message
}

使用接口的解决方案:http://play.golang.org/p/bFysxTFVIH

package main

import "fmt"

func main() {
    err := run()
    if err != nil {
        fmt.Printf("%#v", err)
    }
}

func run() (err error) {
    return check() // <-- 对于直接返回或命名返回值,必须使用 "interface{}"
}

// 使用接口 - Result
func check() Result {
    return nil
}

type Result interface {
    Error() string
}

type result struct {
    message string
}

func (result *result) Error() string {
    return result.message
}
英文:

In a nutshell: don't mix interfaces and pointers/values when using direct or named return values in a function.

The goal is to create an error aggregator, e.g:

func checkStatus() *Result {
		r := &amp;Result{}
		// ... do checks and append to r.message ...
		if r.message == &quot;&quot; {
			return nil
		}
		return r
}

func checkSomething() error {
    return runDiagnostics()
}

func runDiagnostics() *Result {
    ... do something
    // all ok 
    return nil
}

.. somewhere else .... (using named return values)
if err = checkStatus(); err != nil {
	return
}

.. this fails even when runDiagnostics() return nil ...
if err = checkSomething(); err != nil {
    ... err is always &quot;something&quot;
}

detailed check with a pointer http://play.golang.org/p/WPGy_ooXmP

detailed check with an interface http://play.golang.org/p/JjWxEj9WRX

solution with an error indicator http://play.golang.org/p/C30s49GiIN

package main

import &quot;fmt&quot;

func main() {
	err := run()
	if err != nil {
		fmt.Printf(&quot;%#v&quot;, err)
	}
}

func run() (err error) {
	_, err = check()
	return
}

func check() (*Result, error) {
	return nil, nil
}

type Result struct {
	message string
}

func (result *Result) Error() string {
	return result.message
}

solution with an interface http://play.golang.org/p/bFysxTFVIH

package main

import &quot;fmt&quot;

func main() {
	err := run()
	if err != nil {
		fmt.Printf(&quot;%#v&quot;, err)
	}
}

func run() (err error) {
	return check() // &lt;-- for direct return or named return value an &quot;interface{}&quot; must be used
}

// uses an interface - Result
func check() Result {
	return nil
}
   
type Result interface {
	Error() string
}

type result struct {
	message string
}

func (result *result) Error() string {
	return result.message
}

huangapple
  • 本文由 发表于 2014年11月10日 22:01:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/26845572.html
匿名

发表评论

匿名网友

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

确定