期待nil但是得到一个带有nil值的接口,这应该是零

时间:2014-11-10 14:01:54

标签: go

运行它 http://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
}

3 个答案:

答案 0 :(得分:4)

这在FAQGo Traps网站上进行了讨论:

  

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

     

(...)

     

要向调用者返回正确的nil错误,该函数必须返回显式的nil:

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

答案 1 :(得分:2)

Francesc Campoy Flores(来自Google的Go团队)今年在dotGo的his presentation讨论了这个特殊问题。

您可以认为界面值有2个部分;一个类型和一个值。因此,类型*Result和值nil的接口不等于类型和值nil的接口。

您可以通过在比较中输入nil来修复代码:

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
}

答案 2 :(得分:0)

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

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

func checkStatus() *Result {
        r := &Result{}
        // ... do checks and append to r.message ...
        if r.message == "" {
            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 "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() // <-- for direct return or named return value an "interface{}" 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
}