结构不能为零,但这是编译

时间:2014-06-28 09:37:06

标签: go

我正在玩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接口。但是,如果我删除& func中的error并返回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中看到有errnil的检查,所以如果我理解正确,func error完全有可能返回nil在某些情况下。因此MyError struct可能会获取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编译?)

2 个答案:

答案 0 :(得分:9)

在第一个示例中,*MyError实现了error接口。如您所见,它是一个指针,指针可以具有nil值。

var err *MyError
err == nil // true

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

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

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是有效的error,因此是MyError{}变量。但是尝试将这个函数原型改为这个:

func run() MyError

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

答案 1 :(得分:2)

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

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