Golang 1.26 version Feature new(expr) Syntax
Go 1.26 introduces a significant enhancement to the built-in new() function. We will learn it with deep examples in this blog post

Prerequisites: Docker Go 1.26 Environment
Since Golang 1.26 isn’t directly available for public consumption, we will use golang:tip as a stand‑in for Go 1.26 and capture new(expr) demo output into a file, set up Docker and your project like this. Place this Dockerfile at the repo root as Dockerfile
# Use Go tip (development version) which contains upcoming Go 1.26 features
FROM golang:tip
WORKDIR /app
# Copy go.mod and go.sum first for better caching
COPY go.mod go.sum* ./
# Download dependencies (if any)
RUN go mod download 2>/dev/null || true
# Copy source code
COPY . .
# Ensure go.mod is initialized
RUN go mod tidy
# Default command
CMD ["go", "version"]
This pulls the nightly Go tip image, copies your module, installs deps, and ensures go.mod is consistent.
Create docker-compose.yml alongside the Dockerfile.
Note: Do not run this docker-compose file yet, as we need the main.go file, that we will create as we go down the blog post and learn about every line of it.
version: "3.8"
services:
go126:
build:
context: .
dockerfile: Dockerfile
container_name: go126-newexpr
working_dir: /app
volumes:
- .:/app
command: ["sh", "-c", "go run ./main.go > /app/newexpr_output.txt"]
Builds from the Dockerfile using
golang:tip.Mounts your repo into
/appso the output file is visible on the host.Runs
go run ./main.go(yournew(expr)demo) and redirects stdout tonewexpr_output.txtat the project root.
Feature Overview of new(expr)
The new(expr) allocates memory for the expression's type, stores the evaluated result, and returns a pointer to it. Type inference ensures *T where T matches expr. Go 1.26 expects release in February 2026, with this change already in draft notes.
Pre-1.26 required temporary variables or helpers like func intPtr(v int) *int { return &v }, creating boilerplate. Now new(42) directly yields *int pointing to 42.
The main.go file
// Demo 01: new(expr) Syntax
//
// Go 1.26 enhances the built-in new() function to accept expressions,
// not just types. This enables creating pointers to values in a single expression.
package main
import (
"encoding/json"
"fmt"
"time"
)
// Person represents a person with optional age field
type Person struct {
Name string `json:"name"`
Age *int `json:"age,omitempty"` // age if known; nil otherwise
}
// Config represents application configuration with optional fields
type Config struct {
Host string `json:"host"`
Port *int `json:"port,omitempty"`
Timeout *int `json:"timeout,omitempty"`
MaxConns *int `json:"max_connections,omitempty"`
Debug *bool `json:"debug,omitempty"`
}
// yearsSince calculates approximate years since a given time
func yearsSince(t time.Time) int {
return int(time.Since(t).Hours() / (365.25 * 24))
}
// intPtr returns a pointer to an int - OLD WAY
func intPtr(v int) *int {
return &v
}
// boolPtr returns a pointer to a bool - OLD WAY
func boolPtr(v bool) *bool {
return &v
}
func main() {
fmt.Println("==============================================")
fmt.Println("Go 1.26 Feature: new(expr) Syntax")
fmt.Println("==============================================")
fmt.Println()
// ============================================
// Example 1: The Old Way (Pre-Go 1.26)
// ============================================
fmt.Println("--- OLD WAY (Pre-Go 1.26) ---")
fmt.Println()
// Method 1: Temporary variable
age := 30
person1 := Person{
Name: "Alice",
Age: &age, // Need temporary variable
}
fmt.Printf("Method 1 (temp var): %+v\n", person1)
// Method 2: Helper function
person2 := Person{
Name: "Bob",
Age: intPtr(25), // Need helper function
}
fmt.Printf("Method 2 (helper func): %+v\n", person2)
// For config with multiple optional fields - verbose!
port := 8080
timeout := 30
maxConns := 100
debug := true
oldConfig := Config{
Host: "localhost",
Port: &port,
Timeout: &timeout,
MaxConns: &maxConns,
Debug: &debug,
}
fmt.Printf("Old Config: %+v\n", oldConfig)
fmt.Println()
// ============================================
// Example 2: The New Way (Go 1.26)
// ============================================
fmt.Println("--- NEW WAY (Go 1.26) ---")
fmt.Println()
// NOTE: The new(expr) syntax is a Go 1.26 feature.
// When running on Go 1.26+, you can use:
//
// person3 := Person{
// Name: "Charlie",
// Age: new(30), // Direct expression - clean!
// }
//
// For calculated values:
// birthDate := time.Date(1990, 1, 1, 0, 0, 0, 0, time.UTC)
// person4 := Person{
// Name: "Diana",
// Age: new(yearsSince(birthDate)), // Expression!
// }
// Simulating Go 1.26 syntax effect with helper functions
// In Go 1.26, this becomes: new(8080), new(30), new(100), new(true)
newConfig := Config{
Host: "localhost",
Port: intPtr(8080), // Go 1.26: new(8080)
Timeout: intPtr(30), // Go 1.26: new(30)
MaxConns: intPtr(100), // Go 1.26: new(100)
Debug: boolPtr(true), // Go 1.26: new(true)
}
fmt.Printf("New Config: %+v\n", newConfig)
fmt.Println()
// ============================================
// Example 3: JSON Serialization Use Case
// ============================================
fmt.Println("--- JSON SERIALIZATION USE CASE ---")
fmt.Println()
// Perfect for JSON with optional fields
birthDate := time.Date(1990, 5, 15, 0, 0, 0, 0, time.UTC)
calculatedAge := yearsSince(birthDate)
person := Person{
Name: "Eve",
Age: &calculatedAge, // Go 1.26: new(yearsSince(birthDate))
}
jsonData, err := json.MarshalIndent(person, "", " ")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Person JSON:\n%s\n", jsonData)
fmt.Println()
// Person without age (nil pointer omitted in JSON)
personNoAge := Person{
Name: "Frank",
Age: nil,
}
jsonDataNoAge, _ := json.MarshalIndent(personNoAge, "", " ")
fmt.Printf("Person without age:\n%s\n", jsonDataNoAge)
fmt.Println()
// ============================================
// Example 4: Benefits Summary
// ============================================
fmt.Println("--- BENEFITS OF new(expr) ---")
fmt.Println()
fmt.Println("1. ✓ No temporary variables needed")
fmt.Println("2. ✓ No helper functions required")
fmt.Println("3. ✓ Cleaner struct initialization")
fmt.Println("4. ✓ Works with any expression (including function calls)")
fmt.Println("5. ✓ Especially useful for JSON/protobuf optional fields")
fmt.Println()
fmt.Println("==============================================")
fmt.Println("Demo Complete!")
fmt.Println("==============================================")
}
Before Go 1.26
// new() only accepted types
ptr := new(int) // *int pointing to 0
ptr := new(string) // *string pointing to ""
ptr := new(MyStruct) // *MyStruct with zero values
Go 1.26 and Later
// new() can now accept expressions!
ptr := new(42) // *int pointing to 42
ptr := new("hello") // *string pointing to "hello"
ptr := new(calculateAge()) // *int pointing to function result
Why This Matters
The Problem: Optional Pointer Fields
Many serialization libraries (JSON, Protocol Buffers, etc.) use pointers to represent optional values:
type Person struct {
Name string `json:"name"`
Age *int `json:"age,omitempty"` // Optional - nil means "not set"
}
Before Go 1.26, populating such fields was awkward:
// Method 1: Temporary variable (verbose)
age := 30
person := Person{Name: "Alice", Age: &age}
// Method 2: Helper function (boilerplate)
func intPtr(v int) *int { return &v }
person := Person{Name: "Alice", Age: intPtr(30)}
The Solution: new(expr)
// Go 1.26: Clean and direct
person := Person{Name: "Alice", Age: new(30)}
// Works with any expression, including function calls!
person := Person{Name: "Alice", Age: new(calculateAge(birthDate))}
Code Walkthrough
Struct Definitions
type Person struct {
Name string `json:"name"`
Age *int `json:"age,omitempty"` // age if known; nil otherwise
}
type Config struct {
Host string `json:"host"`
Port *int `json:"port,omitempty"`
Timeout *int `json:"timeout,omitempty"`
MaxConns *int `json:"max_connections,omitempty"`
Debug *bool `json:"debug,omitempty"`
}
These structs use pointer fields for optional values. In JSON:
A
nilpointer is omitted from output (due toomitempty)A non-nil pointer includes the value
Old Way: Helper Functions
// intPtr returns a pointer to an int - OLD WAY
func intPtr(v int) *int {
return &v
}
// boolPtr returns a pointer to a bool - OLD WAY
func boolPtr(v bool) *bool {
return &v
}
These helper functions were necessary before Go 1.26. You'd need one for each type!
Old Way: Temporary Variables
age := 30
person1 := Person{
Name: "Alice",
Age: &age, // Need temporary variable
}
The temporary variable age pollutes the scope and adds visual noise.
Old Way: Config with Multiple Optional Fields
port := 8080
timeout := 30
maxConns := 100
debug := true
oldConfig := Config{
Host: "localhost",
Port: &port,
Timeout: &timeout,
MaxConns: &maxConns,
Debug: &debug,
}
This is extremely verbose! Four temporary variables for four optional fields.
New Way: Go 1.26 with new(expr)
// In Go 1.26, this becomes beautifully simple:
newConfig := Config{
Host: "localhost",
Port: new(8080), // Direct value!
Timeout: new(30), // No temp vars!
MaxConns: new(100), // No helpers!
Debug: new(true), // Clean!
}
Calculated Values
The real power shows when using expressions:
func yearsSince(t time.Time) int {
return int(time.Since(t).Hours() / (365.25 * 24))
}
birthDate := time.Date(1990, 5, 15, 0, 0, 0, 0, time.UTC)
// Go 1.26: Pass function result directly to new()
person := Person{
Name: "Eve",
Age: new(yearsSince(birthDate)), // Expression!
}
JSON Serialization Demo
person := Person{
Name: "Eve",
Age: new(35),
}
jsonData, _ := json.MarshalIndent(person, "", " ")
// Output:
// {
// "name": "Eve",
// "age": 35
// }
personNoAge := Person{
Name: "Frank",
Age: nil, // Omitted from JSON due to omitempty
}
// Output:
// {
// "name": "Frank"
// }
Technical Details
How It Works
When the compiler sees new(expr):
It evaluates the expression
exprAllocates memory for a value of that expression's type
Stores the result in the allocated memory
Returns a pointer to that memory
Memory Allocation
new(42) // Allocates int on heap, stores 42, returns *int
new("hi") // Allocates string on heap, stores "hi", returns *string
new(fn()) // Calls fn(), allocates result type, stores result, returns pointer
Type Inference
The type is inferred from the expression:
var a = new(42) // a is *int
var b = new(3.14) // b is *float64
var c = new("hello") // c is *string
var d = new(time.Now()) // d is *time.Time
Best Practices
✅ Do Use new(expr) For
Optional struct fields in JSON/Protobuf
Config{Timeout: new(30)}Passing computed values as pointers
Response{CreatedAt: new(time.Now())}Inline pointer creation
callAPI(new("default-value"))
❌ Avoid When
You need the address of an existing variable
x := 42 ptr := &x // Use & not new() hereCreating zero-valued pointers (use traditional
new(Type))ptr := new(int) // Still valid: *int pointing to 0
Running This Demo
# From repository root
docker compose run --rm go126 go run ./demos/01_new_expr/...
Let me know what you think about this feature
