In Go, what does the composite literal &Point{X: 1} evaluate to?
answer
- One expression, no temporary variable
- Legal even though literals are not variables
- Same result as new, plus field initialisation
- The ampersand does not choose stack or heap
- Inside []*T{...} the ampersand disappears
basics
~20 s&Point{X: 1} allocates a new Point with X set to 1 and the other fields zeroed, and evaluates to a *Point. Go lets you take the address of a composite literal directly, so no temporary variable is needed.
solid answer
~50 s`&Point{X: 1}` builds a fresh `Point` value, initialises the named fields, zeroes the rest, and yields a `*Point` to it. It is exactly equivalent to `p := Point{X: 1}; return &p`, and it is the idiomatic constructor body in Go — `func New() *Encoder { return &Encoder{w: w} }`. Two details are worth knowing. First, the `&` says nothing about where the value lives: escape analysis at compile time decides stack or heap, and `go build -gcflags=-m` will tell you which. Second, inside an outer composite literal whose element type is `*T`, the `&T` may be elided, so `[]*Point{{X: 1}, {X: 2}}` is a two-element slice of distinct `*Point` values. Compared with `new(Point)`, which also returns a `*Point`, the literal form can set fields in the same expression, which is why `&T{...}` has almost entirely displaced `new` for structs.
code
go · 13 linestype Point struct{ X, Y int }
p := &Point{X: 1} // *Point; Y is zero
// longhand for the same thing
tmp := Point{X: 1}
q := &tmp
// element type is *Point, so &Point is elided on each element
pts := []*Point{{X: 1}, {X: 2}}
fmt.Printf("%+v\n", p) // prints &{X:1 Y:0}
_, _ = q, ptsgo deeper
Know that &T{...} is one expression that both builds and addresses a value, and that omitted fields are zeroed. Recognise it as the normal body of a function returning a pointer.
Explain the equivalence with a named temporary, the elision of &T inside a []*T literal, and why &T{...} has displaced new for structs.
Be ready to talk about allocation. Explain that escape analysis, not the ampersand, decides stack versus heap, and show that you have read go build -gcflags=-m output on a real hot path before changing anything.
The judgment here is the API shape, not the syntax: whether a package hands out T or *T is very hard to change once other teams depend on it, so decide it deliberately on mutation and copy semantics rather than habit.
## Taking the address of a literal In most expressions Go's `&` operator requires an addressable operand: a variable, a pointer dereference, a slice element, a field of an addressable struct. A composite literal is a specific, spec-blessed exception — `&T{...}` is legal for a struct, array, slice or map literal, and it means "allocate a new `T`, initialise it from this literal, and give me its address". So `&Point{X: 1}` is shorthand for: ``` tmp := Point{X: 1} ... &tmp ``` with no name for `tmp`. The initialisation rules are the ordinary composite-literal rules: fields you name get your values, fields you omit get their zero values, so `&Point{}` is a pointer to a fully zeroed `Point`. ## Why constructors look like this Go has no constructors, so a package that wants to hand out an initialised value exposes a function that returns one. When the value must be shared, must be mutated by its methods, or is large enough that copying matters, that function returns a pointer, and its body is almost always a single `&T{...}` expression: ``` func NewEncoder(w io.Writer) *Encoder { return &Encoder{w: w} } ``` The alternative spellings all read worse. `new(Encoder)` returns a `*Encoder` but cannot set `w`, so you would need a second statement. A named temporary plus `&tmp` adds a line and a name that carries no information. ## The & does not decide where it lives A very common misreading is that `&` means "heap". It does not. Go's compiler runs escape analysis: if it can prove the value's lifetime ends with the enclosing function's frame, it allocates it on the stack even though you took its address; if the pointer escapes — returned, stored in a longer-lived structure, captured by a goroutine, passed to something the compiler cannot see through — it is heap-allocated. `go build -gcflags=-m` prints the decision per site, in lines such as `&Point{} escapes to heap` or `&Point{} does not escape`. The practical consequence is that a helper that builds `&Point{...}` and only reads it locally can be allocation-free, and that you should measure rather than assume. ## Elision inside an outer literal Inside a composite literal for a slice, array or map, an element that is itself a composite literal may drop the repeated type. Two forms exist and they are easy to conflate: * When the element type is `T`, the inner `T` is elided: `[]Point{{X: 1}, {X: 2}}`. * When the element type is `*T`, the whole `&T` is elided: `[]*Point{{X: 1}, {X: 2}}`. The second is the one that surprises readers, because there is no visible `&` anywhere and yet each element is a distinct pointer to a separately initialised `Point`. The same elision works for map values, so `map[string]*Point{"origin": {}}` is a map to a pointer to a zeroed `Point`. Nothing is shared between the elements: each inner literal allocates its own value. ## Value or pointer? Deciding between returning `T` and `*T` is an API question that outlives the syntax, but the composite-literal forms make each cheap to write. Reach for `&T{...}` when callers must observe mutations made through the value's methods, when the type carries something that must not be copied, or when the value is genuinely large. Reach for `T{...}` when the value is small and behaves like data, since a value costs no indirection and cannot be nil at the call site. ## Checking what you built When a construction site is suspect, print it. `fmt.Printf("%+v\n", p)` on a `*Point` prints `&{X:1 Y:0}` — the `&` prefix tells you it is a pointer, the field names come from `%+v`, and the zeros show which fields your literal did not set. `%#v` goes further and prints the Go-syntax form, `&main.Point{X:1, Y:0}`, which you can paste back into code. Both are the fastest way to confirm that an elided literal inside a bigger structure really initialised the fields you assumed it did. ## Relationship to new `new(T)` and `&T{}` produce the same thing: a pointer to a zeroed `T`. `&T{...}` additionally initialises fields. Modern Go code therefore uses `&T{...}` for structs and keeps `new` for the handful of cases where there is no useful literal — `new(int)`, for instance, when an API wants a `*int` and you have no variable to point at.
- In `[]*Point{{X: 1}, {X: 2}}`, why is there no ampersand?Elision. Inside a composite literal whose element type is `*T`, an element written as a bare literal has its `&T` supplied for you. Each element still allocates its own `Point` and holds a distinct pointer; nothing is shared between them.
- Does &Point{} always allocate on the heap?No. Escape analysis decides at compile time: a value whose pointer never outlives the frame can stay on the stack despite the `&`. Run `go build -gcflags=-m` to see the per-site verdict, printed as either escaping to the heap or not escaping.
- How does &Point{X: 1} differ from new(Point)?Both return a `*Point` to a value that starts out zeroed, and both allocate. The literal form can set fields in the same expression, which `new` cannot, so `&T{...}` is the idiomatic way to build a struct pointer and `new` is mostly reserved for pointers to plain zero values like `new(int)`.
saying these in an interview costs you the question
- Says you cannot take the address of a literal in Go
- Claims &T{} always forces a heap allocation
- Thinks &T{} copies the struct after allocating it
- Believes elements of []*T{{}, {}} share one value
- Says fields omitted from &T{} are left uninitialised