应为nil,但得到一个返回值为nil的接口,该值应该为nil

  • 本文关键字:nil 返回值 一个 接口 应为 go
  • 更新时间 :
  • 英文 :


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

这在常见问题解答和Go Traps网站上进行了讨论:

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

(…)

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

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

Francesc Campoy Flores(来自谷歌围棋团队)在今年的dotGo演讲中谈到了这个特殊的问题。

你可以认为一个接口值有两部分;一个类型和一个值。因此,类型为*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
}

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

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

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
}

最新更新