为什么将<nil>分配给Go?</nil>中的接口类型是有效的

时间:2014-05-01 18:40:10

标签: go

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

package main

import "fmt"

type TesterInterface interface{
    Yell()
}

type Tester struct{}

func (t Tester) Yell() {
    fmt.Println("HELLO")
}

func main() {
    var t TesterInterface
    t = Tester{}
    t.Yell()
    t = nil
    t.Yell()
}

我希望编译器抱怨第19行(t = nil),因为nil没有实现Yell(),但程序运行并提供以下输出:

HELLO
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0xffffffff addr=0x0 pc=0x201a9]

goroutine 1 [running]:
runtime.panic(0xef060, 0x1b3d44)
    /tmp/sandbox/go/src/pkg/runtime/panic.c:266 +0xe0
runtime.panicstring(0x1b3d44, 0x1b85)
    /tmp/sandbox/go/src/pkg/runtime/panic.c:489 +0x120
runtime.sigpanic()
    /tmp/sandbox/go/src/pkg/runtime/os_nacl.c:254 +0x80
main.main()
    /tmpfs/gosandbox-229cdcdf_329829af_705be907_f12cf4dc_ed51e32e/prog.go:20 +0xa9
runtime.main()
    /tmp/sandbox/go/src/pkg/runtime/proc.c:220 +0x1c0
runtime.goexit()
    /tmp/sandbox/go/src/pkg/runtime/proc.c:1394

goroutine 2 [syscall]:
runtime.notetsleepg(0xfeefdf88, 0x0, 0xf8475800, 0xd)
    /tmp/sandbox/go/src/pkg/runtime/lock_sema.c:254 +0xa0
runtime.MHeap_Scavenger()
    /tmp/sandbox/go/src/pkg/runtime/mheap.c:463 +0xc0
runtime.goexit()
    /tmp/sandbox/go/src/pkg/runtime/proc.c:1394
created by runtime.main
    /tmp/sandbox/go/src/pkg/runtime/proc.c:179
 [process exited with non-zero status]

Program exited.

为什么编译器认为将nil分配给接口是合法的?

此外,来自Go-docs:

  

接口类型的变量可以存储任何类型的值,其方法集是接口的任何超集。

“nil”如何包含作为TesterInterface超集的方法集?文档继续说

  

接口类型的未初始化变量的值为nil。

但这似乎与第一个陈述相矛盾。

2 个答案:

答案 0 :(得分:6)

  

The Go Programming Language Specification

     

The zero value

     

当分配内存来存储值时,通过a   声明或调用make或new,并且没有明确的初始化   如果提供,则为存储器提供默认初始化。每   将此类值的元素设置为其类型的零值:false   对于布尔值,0表示整数,0.0表示浮点数,“”表示字符串,nil   用于指针,函数,接口,切片,通道和地图。

nil对接口类型变量有效,因为它是接口类型的零值。

Go Data Structures: Interfaces文章描述了接口值的实现。

答案 1 :(得分:5)

当你提出像#34这样的问题时总是很冒险;为什么编译器认为这是合法的......&#34;像往常一样,答案是因为它是语言,每个接口都接受nil值。但是还有更多的方面:接口的实现,以及如果不是这样,Go将如何工作。

从后者开始,如果nil不是接口的合法值,请考虑问题。你会如何取代常见的习语:

func Something() error {
    ...
    return nil
}

这取决于nil是有效的error这一事实。 同样,指针满足接口是很常见的。如果nil也不满足接口,那么如何返回nil指针?请记住,nil只是一个值。

对于实现,请参阅Why is my nil error value not equal to nil?关键点是接口由类型和值的元组组成,对于每个接口,元组(nil, nil)都有效。

我假设您同意&#34;键入nil&#34; (即某个特定指针类型的nil应该可以分配给由其指针类型实现的接口,对吗?所以接口必须被视为nil,并且必须nil - 以与nil完全相同的方式检查它们 - 检查指针。因此,排除无类型的nil没有安全上的好处,但是您可以打破许多有用的界面(例如返回&#34;没有错误&#34;)。 < / p> <击>

(对于这最后一部分有轻微的细节。再仔细阅读&#34;为什么我的nil错误值不等于nil?&#34;并且你&#39;我会意识到你不能简单地检查一个包含类型nil值的接口。你只能通过将接口断言为其底层指针类型或使用反射来实现这一点。这并没有显着改变这一点,但我并不想掩盖这个问题。)


修改

根据@ newacct的评论,发现了一些无关紧要的观点。

彼得的答案也非常重要。每种类型必须具有零值。接口的零值还有nil