Understanding nil in Go: Variable Definitions, Memory Allocation, and Type Behavior
This article explains the nature of Go's nil keyword, how it interacts with different types such as slices, maps, channels, interfaces, pointers and functions, and clarifies variable definition, zero‑value allocation, make versus var, and the compiler's nil handling semantics.
Go's nil is a predeclared identifier representing the zero value for pointer, channel, func, interface, map, or slice types. It is essentially a variable of a special Type that the compiler uses to trigger specific checks.
The six types that can be compared with or assigned nil are pointer, func, interface, map, slice, and channel. Attempting to use nil with other types results in a compile‑time error.
Variable definitions differ between var and make . Using var only allocates the variable itself (e.g., an 8‑byte pointer for maps or a 24‑byte struct for slices) and zero‑initialises it. Using make also allocates the underlying runtime structure ( hmap , hchan , etc.) via internal functions like makemap or makechan .
For slices, the 24‑byte header ( array pointer, len , cap ) is zero‑allocated, allowing a var slice to be used directly; make only allocates the backing array when needed. The nil check for a slice examines only the array pointer.
Maps allocate an 8‑byte pointer that points to a large hmap structure; only make creates this structure. Nil checks for maps test the pointer value.
Channels behave similarly: a variable holds an 8‑byte pointer to a hchan structure, which is created by makechan . Nil checks test the pointer.
Interfaces consist of two words: a type descriptor and a data pointer. Both var and make (for concrete types) allocate a 16‑byte block that is zero‑initialized. Nil checks examine the data pointer.
Pointers and function variables are simple 8‑byte pointers; assigning nil just zeroes the pointer, and nil checks compare the pointer to zero.
In summary, Go guarantees zero‑value allocation for all variables, but only the six mentioned types can hold nil . The compiler enforces these rules, and runtime functions like mallocgc , makeslice , makemap , and makechan handle the actual memory allocation when make is used.
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { /* ... */ } var slice1 []byte
slice1 = append(slice1, 0x1) var m1 map[string]int
var m2 = make(map[string]int) var c1 chan struct{}
var c2 = make(chan struct{})Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.