Zero Values and Initialization
Overview
Every variable in Go has a value from the moment it’s declared. If you don’t explicitly initialize a variable, it gets a zero value—a sensible default that prevents undefined behavior.
Zero Values by Type
| Type | Zero Value |
|---|---|
bool |
false |
int, int8, … |
0 |
uint, uint8, … |
0 |
float32, float64 |
0.0 |
complex64, complex128 |
(0+0i) |
string |
"" (empty string) |
pointer |
nil |
slice |
nil |
map |
nil |
channel |
nil |
function |
nil |
interface |
nil |
Demonstrating Zero Values
package main
import "fmt"
func main() {
var b bool
var i int
var f float64
var s string
var p *int
var sl []int
var m map[string]int
var ch chan int
var fn func()
var iface interface{}
fmt.Printf("bool: %v\n", b) // false
fmt.Printf("int: %v\n", i) // 0
fmt.Printf("float64: %v\n", f) // 0
fmt.Printf("string: %q\n", s) // ""
fmt.Printf("pointer: %v\n", p) // <nil>
fmt.Printf("slice: %v\n", sl) // []
fmt.Printf("map: %v\n", m) // map[]
fmt.Printf("channel: %v\n", ch) // <nil>
fmt.Printf("func: %v\n", fn) // <nil>
fmt.Printf("interface: %v\n", iface) // <nil>
}Struct Zero Values
Struct fields get their respective zero values:
type User struct {
Name string
Age int
Active bool
Balance float64
}
var user User
// user.Name = ""
// user.Age = 0
// user.Active = false
// user.Balance = 0.0Nested Structs
type Address struct {
Street string
City string
}
type Person struct {
Name string
Address Address // Embedded struct gets zero values too
}
var p Person
// p.Name = ""
// p.Address.Street = ""
// p.Address.City = ""Array Zero Values
Arrays are initialized with zero values for each element:
var arr [5]int // [0, 0, 0, 0, 0]
var strs [3]string // ["", "", ""]
var bools [2]bool // [false, false]Why Zero Values Matter
1. Eliminates Undefined Behavior
// In Go, this is safe and predictable
var count int
count++ // count is now 1
// In C, this would be undefined behavior
// int count;
// count++; // Undefined! Could be anything2. Reduces Boilerplate
// No need for explicit initialization
type Config struct {
Debug bool
Timeout int
Host string
}
cfg := Config{} // All zeros, ready to use selectively
cfg.Host = "localhost" // Only set what differs from zero3. Enables “Usable Zero Value” Pattern
Well-designed types work correctly with zero values:
import "bytes"
// bytes.Buffer works without initialization
var buf bytes.Buffer
buf.WriteString("Hello, ")
buf.WriteString("World!")
fmt.Println(buf.String()) // "Hello, World!"
// sync.Mutex works without initialization
var mu sync.Mutex
mu.Lock()
mu.Unlock()Initialization Methods
Short Variable Declaration
name := "Alice" // Inferred type: string
count := 42 // Inferred type: int
price := 19.99 // Inferred type: float64Explicit Type Declaration
var name string = "Alice"
var count int = 42
var active bool = trueType Inference (var)
var name = "Alice" // Type inferred from value
var count = 42 // intMultiple Variable Declaration
var (
name = "Alice"
age = 30
active = true
)
// Or inline
x, y, z := 1, 2, 3Composite Literal Initialization
// Struct
user := User{
Name: "Alice",
Age: 30,
Active: true,
}
// Partially initialized (rest are zero values)
user := User{Name: "Bob"} // Age: 0, Active: false
// Array
arr := [3]int{1, 2, 3}
// Slice
sl := []string{"a", "b", "c"}
// Map
m := map[string]int{"one": 1, "two": 2}nil vs Zero Value
nil is the zero value for pointers, slices, maps, channels, functions, and interfaces.
nil Slice vs Empty Slice
var nilSlice []int // nil slice
emptySlice := []int{} // empty slice (not nil)
nilSlice == nil // true
emptySlice == nil // false
len(nilSlice) // 0
len(emptySlice) // 0
// Both work with append
nilSlice = append(nilSlice, 1) // [1]
emptySlice = append(emptySlice, 1) // [1]nil Map Danger
var m map[string]int // nil map
// Reading is safe
val := m["key"] // Returns 0 (zero value)
// Writing panics!
m["key"] = 42 // panic: assignment to nil map
// Always initialize before writing
m = make(map[string]int)
m["key"] = 42 // Now safeThe new vs make Functions
new(T)
Allocates zeroed memory and returns a pointer:
ptr := new(int) // *int pointing to 0
*ptr = 42
user := new(User) // *User with zero-valued fields
user.Name = "Alice"make(T, args)
Creates and initializes slices, maps, and channels:
// Slice with length and capacity
sl := make([]int, 5) // [0, 0, 0, 0, 0]
sl := make([]int, 0, 10) // [] with capacity 10
// Map
m := make(map[string]int) // Empty, ready to use
// Channel
ch := make(chan int) // Unbuffered channel
ch := make(chan int, 10) // Buffered channelSummary
| Concept | Description |
|---|---|
| Zero Value | Default value assigned to uninitialized variables |
| Purpose | Eliminates undefined behavior, reduces boilerplate |
new(T) |
Allocates zeroed memory, returns *T |
make(T) |
Creates initialized slice, map, or channel |
nil |
Zero value for pointers, slices, maps, channels, funcs, interfaces |