What is the difference between new() and make()?
Quick Answer
new(T) allocates zeroed memory for a value of type T and returns a pointer to it (*T) — it works for any type, but you almost never need it directly since &T{} does the same thing more idiomatically. make(T, args...) only works on slices, maps, and channels, and it returns an initialized (not zeroed-pointer) value of type T itself, not a pointer — because these three types need internal setup (like allocating a backing array for a slice, or setting up the channel's internal buffer) before they're usable. new([]int) gives you a pointer to a nil slice, which is rarely what you want; make([]int, 5) gives you a ready-to-use slice of length 5.
Detailed Answer
These two built-ins solve related but distinct problems, and mixing them up is a common beginner mistake.
new(T): a pointer to a zero value
p := new(int) // p is *int, pointing to a 0
fmt.Println(*p) // 0
type Point struct{ X, Y int }
pt := new(Point) // pt is *Point, pointing to {0, 0}
new(T) is rarely used directly in idiomatic Go, since &Point{} or &Point{X: 1} reads more naturally and does the same thing for structs.
make(T, ...): initialized slices, maps, channels
s := make([]int, 5) // slice of length 5, all zeros, ready to index
m := make(map[string]int) // empty, writable map
ch := make(chan int, 10) // buffered channel with capacity 10
Why slices/maps/channels need make, not new
var s *[]int = new([]int) // *s is a nil slice — usable for reads, panics/breaks on writes to map-like ops
(*s) = append(*s, 1) // this actually works, since append handles nil slices
var m *map[string]int = new(map[string]int) // *m is a nil map
(*m)["x"] = 1 // panic: assignment to entry in nil map
A slice, map, or channel has internal state (a backing array pointer, a hash table, a buffer) that new's simple zeroing can't set up. make runs the actual initialization logic these types need, and hands back a ready-to-use value, not a pointer to one.
Quick rule of thumb
Use make for slices, maps, and channels. Use struct literals (&T{}) for everything else that needs a pointer. You'll rarely reach for new directly in real Go code.