new and composite literals

Three ways to get a pointer in Go. Two you will type constantly; one you will see in print occasionally and probably never write.

Way 1: take the address of an existing variable

You already know this from Lesson 1:

x := 42
p := &x

Declare a variable, take its address with &, use the pointer. This is the form you reach for most often, because most pointers in real code aim at existing variables: a local, a field inside something else, a slice element.

Way 2: new(T) allocates a fresh zero-valued T

new(T) is a built-in function that allocates a new, zero-valued value of type T and returns a pointer to it:

p := new(int)
fmt.Println(*p)   // 0   (zero value of int)

*p = 42
fmt.Println(*p)   // 42

new(int) is equivalent to var tmp int; p := &tmp, but without the intermediate name. The Go runtime allocates the storage and hands you the pointer in one step.

In practice, you will see new(T) rarely. For primitives, it is hardly ever shorter than just declaring a variable. Its real niche is when you need a pointer to a zero-valued value passed straight into another call, with no binding in between.

Way 3: &T{...} for structs

The third form is the composite literal address, which takes over as the common case once structs arrive in the next chapter:

// You will see this a lot, starting next chapter:
p := &Point{X: 1, Y: 2}

&Point{...} builds a Point with the given field values and returns a pointer to it, all in one step. This is the idiomatic way to allocate a struct in Go. new(Point) would work too, but it gives you a pointer to a zero-valued Point with no fields set, which is rarely what you want.

Why &T{...} wins over new(T) for structs

Two reasons to prefer the composite-literal form once structs arrive:

  1. You can set fields at the same time. &Point{X: 1, Y: 2} tells the reader what the pointer refers to. new(Point) hands you a blank Point and leaves you to fill it in on subsequent lines, which is more code and more places for a mistake.
  2. It matches other Go syntax. Slices use []int{1, 2, 3}, maps use map[string]int{"a": 1}, and structs extend the same pattern: &Point{...} just says "build one and give me the address".

new exists for completeness and for the occasional case where its shorter syntax genuinely helps. You can write real Go code for a long time without typing new once.

Practical hierarchy for getting a pointer
  1. Pointer to a variable you already have: p := &x. Most common.
  2. Pointer to a freshly constructed struct: p := &Point{...}. The next chapter's main pattern.
  3. Pointer to a zero-valued anything, with no intermediate name: p := new(T). Rarely needed; reach for it when neither of the above fits.

When you feel the urge to type new, pause and ask whether a plain variable or a composite literal would read better. Usually one of them does.

Task

Starter is almost empty. In main:

  • Allocate a pointer to a zero-valued int with new(int), bound to p.
  • Write 99 through the pointer with *p = 99.
  • Print *p on its own line (should be 99).
  • Print p == nil on its own line (should be false, because new never returns nil).

Expected output:

99
false
Expected output
99
false