Question
Given a Go struct with an *int64 field, how can you initialize that field with a pointer to the value 0 in a struct literal?
type SomeType struct {
SomeField *int64
}
instance := SomeType{
SomeField: &0, // does not compile
}
Using a conversion also does not compile:
instance := SomeType{
SomeField: &int64(0), // cannot take the address of int64(0)
}
Why do these expressions fail, and what valid Go syntax can create a *int64 whose pointed-to value is 0?
Short Answer
You will learn that pointers in Go must point to addressable storage, while numeric literals and conversion results are temporary values with no address you can take. You will also see the idiomatic ways to initialize *int64 fields, including new(int64), a local variable, a small pointer helper, and a literal-only composite-literal technique.
Concept
Go's address-of operator, &, creates a pointer to an addressable value. An addressable value is one that occupies a stable, named or otherwise addressable location in memory.
A variable is addressable:
var count int64 = 0
p := &count // p has type *int64
A numeric literal such as 0 is not addressable. It is a value written directly in source code, not a variable with storage that Go lets you refer to. Therefore, this is invalid:
p := &0 // invalid: cannot take the address of 0
Likewise, int64(0) is a conversion expression that produces an int64 value, but that result is not an addressable variable:
p := &int64(0) // invalid: cannot take the address of int64(0)
This rule protects Go's pointer model: a pointer must refer to a real value whose lifetime can safely extend beyond the expression that created it.
For a pointer to the zero value of int64, use new(int64). It allocates an int64, initializes it to zero, and returns *int64:
instance := SomeType{
SomeField: new(int64),
}
This distinction matters whenever pointers express optional values. A nil *int64 often means “not provided,” while a non-nil pointer to 0 means “explicitly provided as zero.”
Mental Model
Think of a pointer as a street address.
- A variable is a real house with an address:
&countis valid. - The literal
0is an instruction saying “use zero,” like writing a number on a note. It is not a house, so it has no street address. int64(0)changes the kind of number on the note, but it still does not create a house.new(int64)creates a new house containing the default value0and gives you its address.
Go permits pointers only to things that can reliably have an address.
Syntax and Examples
The field type determines the pointer type you need:
type SomeType struct {
SomeField *int64
}
SomeField needs a value of type *int64, not int and not int64.
Use new for a pointer to zero
instance := SomeType{
SomeField: new(int64),
}
fmt.Println(*instance.SomeField) // 0
new(int64) returns *int64. The allocated int64 starts at its zero value, which is 0.
Use a variable when you have a specific value
value := int64(42)
instance := SomeType{
SomeField: &value,
}
fmt.Println(*instance.SomeField) // 42
The variable value is addressable, so &value is valid.
Use a reusable helper for values other than zero
func Int64Ptr(v int64) *int64 {
return &v
}
instance := SomeType{
SomeField: Int64Ptr(42),
}
Returning &v is safe. Go's compiler ensures the value remains valid after the function returns when its address escapes.
Literal-only expression: possible, but usually not preferred
If an expression must appear inline and you do not want a named helper, index an array literal:
instance := SomeType{
SomeField: &[]int64{0}[0],
}
The array/slice element is addressable, so this produces *int64. Although valid, it is less readable than new(int64) for zero or Int64Ptr(42) for another value.
Note:
&0is invalid for both*intand*int64. An untyped numeric literal does not become addressable merely because a pointer type is expected.
Step by Step Execution
Consider this code:
type SomeType struct {
SomeField *int64
}
func main() {
instance := SomeType{
SomeField: new(int64),
}
*instance.SomeField = 7
}
Step by step:
SomeFieldis declared as*int64, so it must hold an address of anint64value ornil.new(int64)creates storage for oneint64value.- Go initializes that storage to the
int64zero value:0. new(int64)returns the address of that storage, with type*int64.- The struct field receives that pointer.
*instance.SomeFielddereferences the pointer and accesses the actualint64value.- The assignment changes the pointed-to value from
0to7.
By contrast, &int64(0) fails before the program runs: int64(0) is a temporary expression result, not an addressable location.
Real World Use Cases
Pointers to numeric values are useful when zero and absence mean different things.
- HTTP API request payloads:
nilcan mean “do not change this field,” whileInt64Ptr(0)can mean “set it to zero.” - Database updates: a
NULLdatabase value may map tonil, while a real database value of0maps to a non-nilpointer. - Configuration:
nilmay mean “use the default timeout,” while a pointer to0may mean “disable the timeout.” - JSON serialization: with tags such as
omitempty, a non-nil*int64whose value is0can be included, while anilpointer can be omitted. - Generated API clients: OpenAPI and protobuf-related code frequently uses pointer fields to represent optional scalar values.
Example of preserving this distinction:
type UpdateRequest struct {
RetryLimit *int64
}
func Int64Ptr(v int64) *int64 { return &v }
noChange := UpdateRequest{
RetryLimit: nil,
}
setToZero := UpdateRequest{
RetryLimit: Int64Ptr(0),
}
_ = noChange
_ = setToZero
Real Codebase Usage
In production Go code, choose the clearest approach for the value you need.
Use new(T) for the zero value
settings := SomeType{SomeField: new(int64)}
This is concise and clearly communicates “a non-nil pointer to zero.”
Use a typed pointer helper for explicit values
func Int64Ptr(v int64) *int64 { return &v }
settings := SomeType{SomeField: Int64Ptr(30)}
Projects often collect helpers like StringPtr, BoolPtr, and Int64Ptr in a small package. In Go 1.18 and later, a generic helper can serve all types:
func Ptr[T any](v T) *T { return &v }
settings := SomeType{SomeField: Ptr(int64(30))}
Validate pointers with guard clauses
Before dereferencing a pointer, check whether it is nil when nil is allowed:
func retryLimit(input *int64) int64 {
if input == nil {
return 3
}
return *input
}
This prevents a runtime panic from dereferencing nil.
Avoid shared mutable pointer values accidentally
If multiple structs use the same pointer, modifying one affects what the others observe:
value := int64(0)
a := SomeType{SomeField: &value}
b := SomeType{SomeField: &value}
*a.SomeField = 9
fmt.Println(*b.SomeField) // 9
Create separate pointers when each struct must own an independent value.
Common Mistakes
Taking the address of a literal
p := &0
This does not compile because literals are not addressable. Use a variable, new(int64) for zero, or a pointer helper.
Taking the address of a conversion result
p := &int64(0)
This also does not compile. The conversion produces a value, not an addressable variable.
Using the wrong integer type
instance := SomeType{
SomeField: new(int), // invalid: *int is not *int64
}
Pointer types must match exactly. *int, *int32, and *int64 are distinct types.
Use:
instance := SomeType{
SomeField: new(int64),
}
Confusing nil with a pointer to zero
instance := SomeType{SomeField: nil}
This is valid, but it does not point to 0. Dereferencing it panics:
fmt.Println(*instance.SomeField) // panic if SomeField is nil
Use new(int64) when a non-nil pointer to zero is required.
Taking the address of a map element
values := map[string]int64{"limit": 0}
p := &values["limit"] // invalid
Map elements are not addressable because the map may move or replace their storage internally. Copy the value to a variable first:
value := values["limit"]
p := &value
Reusing one pointer unintentionally
A pointer shares one underlying value. If independent values are required, make a new variable or call the helper separately for each field.
Comparisons
| Expression or approach | Result | When to use |
|---|---|---|
nil | A nil *int64 | The value is absent or unspecified. |
new(int64) | A non-nil *int64 pointing to 0 | You specifically need a pointer to the zero value. |
&value where value is int64 | A pointer to an existing variable | The value already exists or needs a meaningful name. |
Int64Ptr(42) | A *int64 pointing to 42 | Clean, reusable inline initialization. |
Ptr(int64(42)) | A *int64 pointing to 42 | Generic helper in Go 1.18+. |
&[]int64{42}[0] | A *int64 pointing to 42 | Valid inline syntax, but usually less readable. |
&0 | Compile error | Literals are not addressable. |
&int64(0) | Compile error | Conversion results are not addressable. |
new(int64) and Int64Ptr(0) both create a non-nil pointer to zero. The main difference is intent: new(int64) is ideal for zero, while Int64Ptr(v) reads naturally when the value is explicit.
Cheat Sheet
// Field requiring a pointer to int64
type SomeType struct {
SomeField *int64
}
// Non-nil pointer to int64 zero
x := SomeType{SomeField: new(int64)}
// Pointer through an addressable variable
v := int64(42)
x = SomeType{SomeField: &v}
// Reusable helper
func Int64Ptr(v int64) *int64 { return &v }
x = SomeType{SomeField: Int64Ptr(42)}
// Generic helper (Go 1.18+)
func Ptr[T any](v T) *T { return &v }
x = SomeType{SomeField: Ptr(int64(42))}
// Valid but less idiomatic literal-only form
x = SomeType{SomeField: &[]int64{42}[0]}
Rules:
&requires an addressable operand.- Variables and array/slice elements are addressable.
- Numeric literals, conversion results, and map elements are not addressable.
*intand*int64are different, incompatible types.nilis not the same as a pointer to0.- Check a pointer for
nilbefore dereferencing it when nil is possible.
FAQ
Why does &int64(0) not compile in Go?
int64(0) is a conversion expression that produces a temporary value. Go permits & only on addressable values, such as variables and array or slice elements.
Does &0 work for *int in Go?
No. 0 is a numeric literal and is not addressable, regardless of whether the desired type is *int, *int64, or another pointer type.
What is the shortest way to create a *int64 pointing to zero?
Use new(int64):
p := new(int64)
It returns a non-nil *int64 whose value is 0.
Is returning &v from func Int64Ptr(v int64) *int64 safe?
Yes. Go ensures that v remains allocated for as long as the returned pointer can be used. You do not get a dangling pointer.
Can I use new(int64) for a value other than zero?
Yes, but assign through the returned pointer afterward:
p := new(int64)
*p = 42
For inline nonzero values, a helper such as Int64Ptr(42) is usually clearer.
What does nil mean for an *int64 field?
It means the field does not point to any int64 value. Whether that represents “missing,” “unknown,” or “use a default” depends on your program's design.
Why is &[]int64{0}[0] valid?
The indexing expression refers to an element in a newly created slice backing array. That element is addressable, unlike the standalone literal 0. It is legal but generally less readable than new(int64) or a helper function.
Mini Project
Description
Build a configuration constructor for a worker service. The configuration uses *int64 so it can distinguish an omitted retry limit from an explicitly configured retry limit of 0. This models a common API and configuration pattern.
Goal
Create configurations that represent default behavior, an explicitly disabled retry limit, and a custom retry limit without unsafe pointer handling.
Requirements
Use a struct with an *int64 retry-limit field.
Create one configuration where the retry limit is omitted (nil).
Create one configuration where the retry limit is explicitly 0.
Create one configuration where the retry limit is 5.
Write a function that resolves nil to a default value and safely dereferences non-nil values.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.