Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it valid to assign <nil> to an interface type in Go?

Tags:

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()
}

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.

like image 349
Darrrrrren Avatar asked May 01 '14 18:05

Darrrrrren


2 Answers

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.

like image 172
peterSO Avatar answered Sep 20 '22 02:09

peterSO


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?

like image 43
Rob Napier Avatar answered Sep 22 '22 02:09

Rob Napier