【问题标题】:Structs cannot be nil, but this is compiling结构不能为零,但这是编译
【发布时间】:2014-08-19 08:53:22
【问题描述】:

我正在玩 Go Playground 并找到以下代码:

package main

import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}

所以在这里我可以看到*MyError 实现了error 接口。但是,如果我在 error func 中删除 & 并返回 MyError,我会收到编译时错误: prog.go:19: cannot use MyError literal (type MyError) as type error in return argument: MyError does not implement error (Error method has pointer receiver)。好的,我可以理解,所以我可以像这样创建函数Error,这将编译并运行成功:

func (e MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return MyError{
        time.Now(),
        "it didn't work",
    }
}

然后我在main func 中看到检查err 是否为nil,所以如果我理解正确的话,func error 在某些情况下完全有可能返回nil。因此MyError 结构可以采用nil 值。但是如果我尝试编译这个:

import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return nil
    return MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    var err2 MyError = nil

    fmt.Println(err2)
    if err := run(); err != nil {
        fmt.Println(err)
    }
}

go 编译器说:prog.go:27: cannot use nil as type MyError in assignment [process exited with non-zero status]

为什么在大写的情况下编译成功而在这种情况下编译失败? 结构是否有可能是nil(我猜不是,但是为什么run func 编译?)

【问题讨论】:

    标签: go


    【解决方案1】:

    在第一个示例中,*MyError 实现了error 接口。可以看到,它是一个指针,一个指针可以有nil的值。

    var err *MyError
    err == nil // true
    
    var err *MyError = new(MyError)
    err == nil // false
    

    但在第二个例子中,实现error接口的是MyError,它不再是指针了。

    var err MyError
    err == MyError{} // true
    &err == nil // false
    err == nil // Compilation error
    

    这次是err的地址可以是nil,而不是变量本身。


    作为比较,考虑int 类型:var i int。例如,您可以检查i == 0,但如果测试i == nil 将是错误的,因为nil 不是整数(正如它之前不是MyError)。但是你仍然可以检查i的地址是否为nil&i == nil


    编辑

    请注意,此函数将始终返回 nil(执行在第一个 return 之后立即停止):

    func run() error {
        return nil
        return MyError{
            time.Now(),
            "it didn't work",
        }
    }
    

    它编译是因为函数的原型告诉它必须返回一个error,事实上,nil 是一个有效的errorMyError{} 变量也是如此。但是试着把这个函数原型改成这个:

    func run() MyError
    

    您将看到编译失败,因为nil 不是MyError 变量,即使它是error 类型的有效值。

    【讨论】:

    • 我明白这一点,但看看run func。它返回 MyError 但也可以返回 nil。
    • 啊哈,现在说得通了。谢谢你。 :)
    【解决方案2】:

    nil 接口与包含 nil 值的接口不同。

    在您的问题中,您已经看到一个接口(在您的情况下是内置错误接口)可以为 nil,并得出了一个错误的结论,即每种可以实现该接口的类型也可以为 nil。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-16
      • 2018-09-23
      • 2021-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多