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()
}
I would expect the compiler to complain about line 19 (t = nil
), since nil
doesn't implement Yell()
, however the program runs and provides the following output:
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.
Why does the compiler consider it legal to assign nil
to an interface?
Additionally, from the Go-docs:
A variable of interface type can store a value of any type with a method set that is any superset of the interface.
How does "nil" contain a method set that is a superset of TesterInterface
? The docs go on to say that
The value of an uninitialized variable of interface type is nil.
But this seems to contradict the first statement.
The Go Programming Language Specification
The zero value
When memory is allocated to store a value, either through a declaration or a call of make or new, and no explicit initialization is provided, the memory is given a default initialization. Each element of such a value is set to the zero value for its type: false for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps.
The value nil
is valid for an interface type variable because it's the zero value for the interface type.
The Go Data Structures: Interfaces article describes the implementation of interface values.
It is always dicey when you ask questions like "why does the compiler consider it legal…" The answer, as usual, is because that's the language, and every interface accepts the nil
value. But there are more sides of this: the implementation of interfaces, and how Go would work if it were not this way.
Starting with the latter, consider the problem if nil
were not a legal value for interfaces. How would you replace the common idiom:
func Something() error {
...
return nil
}
This relies on the fact that nil
is a valid error
. Similarly, it is very common that pointers satisfy an interface. If nil
did not also satisfy the interface, how would you return the nil
pointer? Remember, nil
is just a value.
To the implementation, see Why is my nil error value not equal to nil? The key point is that an interface is made up of a tuple of type and value, and for every interface, the tuple (nil, nil)
is valid.
I assume you agree that a "typed nil
" (i.e. a nil
of some specific pointer type) should be assignable to an interface that is implemented by its pointer type, correct? So interfaces must be treated as though they could be nil
, and you must nil
-check them in exactly the same ways that you nil
-check pointers. So there is no safety benefit to excluding the untyped nil
, but you'd break many useful things you can do with interfaces (like return "no error").
(There's a slight subtlty to this last part. Read carefully again "Why is my nil error value not equal to nil?" and you'll realize that you can't trivially nil-check an interface that holds a typed nil value. You can only do that by type asserting the interface into its underlying pointer type or using reflect on it. This doesn't dramatically change the point, but I didn't want to gloss over that problem.)
EDIT
Based on @newacct's comments below, struck some extraneous points.
PeterSO's answer is also very important. Every type must have a zero value. What else would the zero value for an interface be but nil
?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With