How Go 1.26’s new Built‑in ‘new’ Accepts Any Expression – Simplify Code and Boost Performance
The article explains the background and need for helper functions that return pointers to values in Go, introduces the upcoming Go 1.26 extension that lets the built‑in new function accept arbitrary expressions, shows practical code examples, demonstrates memory‑leak avoidance and performance gains, and concludes with a brief outlook on the feature’s release.
Go 1.26 is adding a new capability to the built‑in new function: it will accept an arbitrary expression, copy the result into a temporary variable of the same type, and return a pointer to that variable.
Why a helper function was needed
In many large Go projects (e.g., Kubernetes) developers frequently need a pointer to a value for JSON, gRPC, or YAML handling. Since Go forbids taking the address of a literal or constant, a common pattern is to write a small helper that copies the value and returns its address:
func getPointerValue[T any](v T) *T {
return &v
}Using this helper, a struct field can be set as d.Num = getPointerValue(12345) instead of the illegal d.Num = &12345.
Memory‑leak concerns
If a pointer to a field of a large struct or an element of a large slice is stored, the entire object stays alive because the garbage collector sees a live reference. For example, assigning d1.Num = &bigObj.Num or d2.Num = &bigSlice[1000] prevents bigObj and bigSlice from being freed, effectively causing a memory leak.
The helper function solves this by copying the value first, so the pointer points to a small temporary variable rather than the large object.
The new new extension
Go 1.26 extends new so that it can take any expression. The compiler rewrites the call by creating a temporary variable, copying the expression result into it, and returning its address. This eliminates the extra function call and gives the compiler more information for escape analysis.
Examples:
// Simple literal
p := new(1234) // *int, value 1234
// Using a function result
func getString() string { return "apocelipes" }
s := new(getString()) // *string, value "apocelipes"
// Combining expressions
s := "Hello, "
p := new(s + getString() + "!") // *string, value "Hello, apocelipes!"Performance comparison
Benchmarks show that the helper incurs an extra heap allocation and is slower because the compiler treats the helper conservatively during escape analysis. The built‑in new call, however, is specially handled: the temporary variable can be allocated on the stack, avoiding the heap allocation and improving speed.
func BenchmarkOld(b *testing.B) {
for b.N > 0 {
p := getPointerValue(123)
if p == nil || *p != 123 {
b.Fatal()
}
}
}
func BenchmarkNew(b *testing.B) {
for b.N > 0 {
p := new(123)
if p == nil || *p != 123 {
b.Fatal()
}
}
}Conclusion
Starting with Go 1.26, new can accept any expression, allowing developers to drop custom pointer‑helper functions, write clearer code, and benefit from better performance thanks to the compiler’s special handling of the built‑in.
Go Development Architecture Practice
Daily sharing of Golang-related technical articles, practical resources, language news, tutorials, real-world projects, and more. Looking forward to growing together. Let's go!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
