Today’s topic: Allocation!
What? You thought allocation was relegated to languages like C and C++?
Yeah, that’s actually pretty close to true. We’re not learning about malloc today. Rather, we’re learning about the built-in function new
, which gives us just one (of many!) ways to allocate memory for a variable.
Allocation
The built-in function new takes a type
T
, allocates storage for a variable of that type at run time, and returns a value of type*T
pointing to it. The variable is initialized as described in the section on initial values.new(T)
For instance
type S struct { a int; b float64 } new(S)
allocates storage for a variable of type
S
, initializes it(a=0, b=0.0)
, and returns a value of type*S
containing the address of the location.
A natural, and expected, question after reading that, would be: Okay, but when would I use new
, and why?
Before I answer that, let’s look at some equivalents and near equivalents.
type S struct { a int; b float64 }
x1 := &S{}
x2 := new(S)
var y *S
var z S
x3 := &z
In this code, x1
, x2
, and x3
are equivalent. They’re all pointers to an instance of the zero value of S
. (They aren’t equal, in that they all point to distinct instances, so their pointer values are different.)
Of note, y
, is not the same. It is of the same type, but it is actually a nil
pointer of type *S
.
So now then, let’s get back to when and why to use new
as opposed to these alternatives.
First, var z S; x3 := &z
is a rather cumbersome way to initialize x3
, so it quickly falls out of the running. That leaves &S{}
and new(S)
. Which to choose?
This mostly boils down to preference, in the majority of cases. My preferences is usually for the &S{}
syntax. Most important, it’s easier to expand, if I later decide to initialize any of its members to a non-zero value:
x1 := &S{a: 3}
And that relates to the second point I’ll make, and when I might choose new
as an alternative: If I want to convey semantically that the value I’m creating is explicitly a pointer to the zero value, I might choose new
. It’s rare that I want to do this. It’s rare that I use new
. Except…
The one exception to all this, is the one time when new
is the only good option. When is that?
When you’re trying to create a new value within a generic function.
func Foo[T any]() (T, error) {
/* Some logic */
if err != nil {
return nil, err
}
/* more logic */
}
This above code won’t work, because nil
is not a valid value for all members of T
. T
might be a string
or int
, which cannot be nil
. So how do we return the correct zero value of T
?
Technically, there are options, but the most concise one is:
return *new(T), err
Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)