Go 1.26's new(expr): initializing pointers in one step
How the built-in new function gains expression support — and what go fix does about your existing Ptr helpers.
It's common in Go codebases to see a small helper file — ptr.go, helpers.go, internal/util/pointer.go — that contains some version of this:
go
func Ptr[T any](v T) *T { return &v }Before generics, the same file held a family of typed wrappers — StringPtr, IntPtr, BoolPtr. They all exist for the same reason: in Go, the address-of operator needs a named storage location — a variable, a struct field, an element of an array — to point at. A bare literal like 42 or "debug" doesn’t have one, so &42 doesn’t compile.
Go 1.26 adds a direct way to do this through the built-in new function.
What new(expr) does
new has always taken a type and returned a pointer to its zero value. In 1.26, it also takes an expression, allocates a variable of the inferred type, and initializes it to the expression’s value. The language spec puts it precisely:
If the argument
expris an expression of typeT, thennew(expr)allocates a variable of typeT, initializes it to the value ofexpr, and returns its address, a value of type*T.
In practice:
go
p := new(42) // *int pointing to 42
q := new("debug") // *string pointing to "debug"
r := new(true) // *bool pointing to trueThe official release notes show a particularly useful pattern — taking a pointer directly from a function result:
go
type Person struct {
Name string `json:"name"`
Age *int `json:"age"`
}
p := Person{
Name: name,
Age: new(yearsSince(born)),
}Worth knowing what isn’t new: pointers to composite literals already worked. &Person{Name: "alice"} has always compiled. The change in 1.26 specifically helps with simple-typed values — primitives, function results, type-converted expressions like int64(300).
Where it helps
JSON, mainly. Optional fields in PATCH payloads use pointers so that nil (omitted) is distinguishable from a zero value (explicitly false, 0, ""). Building a partial update used to mean temporary variables for each field:
go
level := "debug"
return ServiceConfig{
LogLevel: &level,
Replicas: nil,
Suspended: nil,
}With new(expr):
go
return ServiceConfig{
LogLevel: new("debug"),
Replicas: nil,
Suspended: nil,
}The construction reads as cleanly as the type definition — no scaffolding around the values themselves.
The migration story
1.26 also ships a rewritten go fix. It now runs the same analyzer framework as go vet, and the modernize package includes an analyzer named newexpr for this exact case.
What it does: scans the codebase for Ptr-style wrapper functions, annotates them with //go:fix inline, and a second pass rewrites every callsite. Running go fix ./... handles the migration. The wrapper functions stay (with the directive comment) so existing code keeps compiling; once every caller has been rewritten, they can be removed in a follow-up.
This is what tends to make a syntax change spread quickly. for range over integers, min/max, and slices.Contains all moved through the ecosystem on the back of similar tooling.
A few practical notes:
The
Ptrhelper becomes optional —go fix rewrites the callsites for you, and the wrapper itself can be removed once nothing depends on it.Pointer-to-expression patterns become natural.
new(yearsSince(t)),new(int64(req.Limit)),new(strings.ToUpper(name))— values that didn’t really need a name now don’t have to have one.Optional struct fields read more cleanly. The construction matches the type definition without intermediate variables.
Performance is unchanged from a Ptr helper. Go’s escape analysis decides whether new(expr)‘s result lives on the stack or the heap, exactly as it did when calling a generic wrapper. Where Ptr(42) stayed on the stack, new(42) stays on the stack. The runtime did get faster at small allocations in 1.26 — the Green Tea garbage collector is now on by default — but that’s a separate change in the same release, not a consequence of new(expr).
A small, well-scoped ergonomic improvement. The kind that quietly tidies up a lot of helper files across the ecosystem.
If this is the kind of thing you want more of — Go internals, the toolchain, the runtime, all of it built around shipping real web services — the Go track on Project Lighthouse is project-based end to end. HTTP, JSON, databases, observability, deployment, one working program per chapter.
Visit https://projectlighthouse.io/books .
Subscribe to get the next post in your inbox.




