Saturday, August 15, 2026
Why Does new() Behave Differently From a Struct Literal in Go?
Posted by

Why Does new() Behave Differently From a Struct Literal in Go?
new(T) allocates a zero T and returns *T. A struct literal T{...} produces a value of type T. &T{...} takes the address of that value — that form is what you want when you need a pointer and field values. new(T) cannot take field initializers. new(T) is also not nil; var p *T is.
For a zero struct, new(User) and &User{} are the same idea. The confusion is mixing those with User{} (a value) and with new on maps.
The failure
package main
import "fmt"
type User struct {
Name string
Age int
}
func main() {
a := User{Name: "Ada"} // User
b := new(User) // *User, Name == "", Age == 0
c := &User{Name: "Ada"} // *User, Name == "Ada"
fmt.Printf("%T %+v\n", a, a)
fmt.Printf("%T %+v\n", b, b)
fmt.Printf("%T %+v\n", c, c)
// new(User{Name: "Ada"}) // does not compile
}
People then write new(map[string]int) and panic on write: new zeroed a nil map, it did not make a hash table.
m := new(map[string]int)
(*m)["k"] = 1 // panic: assignment to entry in nil map
Why this happens
new is not a constructor. The spec: allocate a variable of type T, zero it, return its address. No constructor arguments. For a struct that means every field is the type's zero: "", 0, nil pointers/slices/maps.
A composite literal builds the value in one expression. Keyed fields you omit are still zero; fields you name are set. Prefix & and the type of the expression is *T. Go's sugar for &User{Name: "Ada"} is "create the value, take its address." Same as:
u := User{Name: "Ada"}
p := &u
except the composite form does not force you to name the temporary.
var p *User does not call new. It leaves p == nil. Dereferencing that panics. new(User) never returns a nil pointer.
make is a different builtin. It initializes the runtime header for slices, maps, and channels. new does not.
The fix
- Need a value:
User{Name: "Ada"}(prefer keyed fields). - Need a pointer with fields set:
&User{Name: "Ada"}. - Need a pointer to a zero
T(including non-struct types):new(T)or&T{}for structs. - Need a usable map/slice/channel:
make(...).
u := &User{Name: "Ada", Age: 36}
m := make(map[string]int)
m["k"] = 1
Do not pick new vs &T{} for performance. Escape analysis decides stack vs heap either way.
Learn more: