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.
但这似乎与第一个陈述相矛盾.
Go编程语言规范
零值
当分配内存来存储值时,无论是通过声明还是调用make或new,并且没有提供显式初始化,内存都会被赋予默认初始化.这种值的每个元素都设置为其类型的零值:布尔值为false,整数为0,浮点数为0.0,字符串为"",指针,函数,接口,切片,通道和映射为nil.
该值nil
对于接口类型变量有效,因为它是接口类型的零值.
在围棋的数据结构:接口介绍了接口值的实现.
当你提出诸如"为什么编译器认为它合法......"这样的问题时总是很冒险.答案就像往常一样,因为那是语言,每个接口都接受这个nil
值.但是还有更多的方面:接口的实现,以及如果不是这样,Go将如何工作.
从后者开始,如果nil
不是接口的合法值,请考虑问题.你会如何取代常见的习语:
func Something() error { ... return nil }
这取决于nil
有效的事实error
.类似地,指针满足接口是很常见的.如果nil
还没有满足界面,你会如何返回nil
指针?记住,nil
这只是一个价值.
对于实现,请参阅为什么我的nil错误值不等于nil?关键是接口由类型和值的元组组成,对于每个接口,元组(nil, nil)
都是有效的.
我假设你同意"typed nil
"(即nil
一些特定的指针类型)应该可以分配给由其指针类型实现的接口,对吗?因此必须将接口视为可能的接口nil
,并且必须nil
以与检查nil
指针完全相同的方式检查接口.因此排除无类型的安全性没有任何好处nil
,但是你可以打破许多有用的东西(比如返回"无错误").
(这最后一部分有一点轻微的意思.再仔细阅读"为什么我的nil错误值不等于nil?"你会发现你不能轻易地检查一个包含nd值的接口.只能通过类型将接口断言为其底层指针类型或使用反射来实现.这并没有显着改变这一点,但我不想掩盖这个问题.)
编辑
根据@ newacct下面的评论,我们发现了一些无关紧要的观点.
PeterSO的回答也非常重要.每种类型必须具有零值.接口的零值还有什么nil
呢?