new, &T{} and make
new(T) and &T{} both hand back a pointer to a fresh zeroed value; make builds slices, maps and channels and nothing else.
new(T) *T | &T{...} *T | make([]T, len, cap) | make(map[K]V) | make(chan T, n)Go gives you three ways to create a value, and they are not interchangeable. Knowing which belongs to which type removes a whole class of confusing errors.
new and &T{}
new(T) allocates space for a value of type T, sets every field to its zero value, and returns the address of it: a *T. &T{} does exactly the same thing, written as a composite literal with its address taken. They are the same thing, so prefer the literal, because the literal can also fill fields in: &Point{X: 3, Y: 4}. new stays useful for types that have no fields, such as new(int).
make
make works on exactly three types: slices, maps and channels. A channel is a pipe that carries values between parts of a running program; a later stage uses them. These three need setup beyond zeroing, so make builds the internal structure and returns a usable value of the type itself, not a pointer to it.
make([]int, 3)gives a slice of three zeros;make([]int, 0, 3)gives an empty one with room for three.make(map[string]int)gives an empty map; a second argument is a size hint, not a limit.make(chan string, 1)gives a channel that can hold one value.
What trips people up
The zero value of a slice is nil, and append works on a nil slice, so var s []int is often enough. The zero value of a map is also nil, but writing to a nil map stops the program. A map must be made first.
Example
package main
import "fmt"
type Point struct {
X, Y int
}
func main() {
// new(T) allocates a zeroed T and returns its address.
a := new(Point)
// &T{} is the same thing, and can set fields while it is at it.
b := &Point{}
c := &Point{X: 3, Y: 4}
fmt.Println(*a, *b, *c)
// make is only for slices, maps and channels, and it returns the
// ready-to-use value itself rather than a pointer to it.
nums := make([]int, 0, 2)
nums = append(nums, 7)
ages := make(map[string]int)
ages["ada"] = 36
done := make(chan string, 1)
done <- "ok"
fmt.Println(nums, ages["ada"], <-done)
}
// Output:
// {0 0} {0 0} {3 4}
// [7] 36 ok
When to use it
- Write a NewServer function that returns *Server with defaults already filled in.
- Pre-size a buffer with make([]byte, 0, 4096) before reading a file into it.
- Create the map a cache struct needs before anything writes to it.
- Make a channel with room for one value to carry a completion signal.
More examples
A nil map can be read but not written
This is the shape of the bug behind most "assignment to entry in nil map" reports: a struct has a map field, nobody made it, and reads keep working so the mistake surfaces much later at the first write. When a type owns a map, make it in the function that builds the type.
package main
import "fmt"
func main() {
var counts map[string]int // nil: no storage behind it yet
// Reading is allowed and gives the zero value for the type.
fmt.Println(counts == nil, len(counts), counts["missing"])
// counts["a"] = 1 here would stop the program.
counts = make(map[string]int, 4) // 4 is a size hint, not a maximum
counts["a"] = 1
fmt.Println(counts == nil, len(counts), counts["a"])
}
// Output:
// true 0 0
// false 1 1
Length, capacity, and when make earns its place
Use make([]T, n) when you want n slots to index into straight away, and make([]T, 0, n) when you know roughly how many items you will append and want to avoid repeated regrowth. If you know neither, var s []T plus append is the simplest correct choice.
package main
import "fmt"
func main() {
var zero []int // nil slice, but append works on it
zero = append(zero, 1)
sized := make([]int, 3) // three elements, each the zero value 0
sized[2] = 9
room := make([]int, 0, 3) // no elements yet, room for three
room = append(room, 1, 2, 3)
fmt.Println(zero, sized, len(room), cap(room))
}
// Output:
// [1] [0 0 9] 3 3
A constructor function returning a pointer
This is the standard Go constructor: a plain function named NewX that returns &X{...} with defaults already applied. Reach for it whenever a type has fields that must be set up before use, instead of hoping callers remember to set them.
package main
import "fmt"
type Server struct {
Addr string
Timeout int
}
// NewServer returns a pointer, so every caller works on the same Server.
func NewServer(addr string) *Server {
return &Server{Addr: addr, Timeout: 30}
}
func main() {
s := NewServer("localhost:8080")
s.Timeout = 5
fmt.Println(s.Addr, s.Timeout)
n := new(int) // new is still the shortest way to get a *int
*n = 42
fmt.Println(*n)
}
// Output:
// localhost:8080 5
// 42
Discussion