make — allocates and initializes internal data structures for slices, maps, and channels. new — allocates zeroed storage and returns a pointer, but does not initialize complex types.
make is the only way to create a usable slice, map, or channel. It returns the type itself (not a pointer) and sets up the internal bookkeeping — for slices it allocates the backing array and sets length/capacity, for maps it creates the hash table, for channels it allocates the ring buffer.
1s := make([]int, 0, 100) // len=0, cap=100 — ready to append2m := make(map[string]int) // initialized, safe to write to3ch := make(chan int, 10) // buffered channel with cap=10
new(T) returns *T pointing to a zero-valued T. For simple types like int or bool this works fine, but for slices/maps/channels the zero value is nil — you still need make to make them usable.
1p := new(int) // *int pointing to 02*p = 4234q := new([]int) // *[]int — points to nil slice5*q = make([]int, 0, 10) // must call make
When to use:
Common mistakes:
new([]int) and then append without make — you get a nil slice panic.make([]int, 10) (len=10, all zeros) with make([]int, 0, 10) (len=0, cap=10).make on a non-slice/map/channel type — compile error.