Searches such as golang pass by reference, go pass by reference, or golang call by reference usually mean “can the callee change my variable?” In Go the precise answer to is go pass by value or reference is: every function argument is passed by value. What changes is what that value is—an integer, a struct bit pattern, or a pointer address—so golang pass by reference in everyday speech maps to passing a *T (see pointers in Go). Slices and maps feel like pass by reference golang because the copied header or map handle still aliases the same underlying data.
Tested with Go 1.24 on Linux.
Primitives and structs: go pass by value
Calling f(x) copies x. For int, string, or a struct, the callee cannot reassign the caller’s variable; only the copy changes.
package main
import "fmt"
func swapCopy(a, b int) {
a, b = b, a
}
func swapPtr(a, b *int) {
*a, *b = *b, *a
}
func main() {
x, y := 1, 2
swapCopy(x, y)
fmt.Println("after copy swap:", x, y)
swapPtr(&x, &y)
fmt.Println("after pointer swap:", x, y)
}You should see after copy swap: 1 2 then after pointer swap: 2 1. The pointer parameters are still passed by value—the values are the addresses of x and y.
Slices and maps: golang pass by reference or value in practice
Slices
A slice value is a small header (pointer, length, capacity). It is passed by value, but the pointer inside points at a shared backing array, so changing s[i] in a function is visible to the caller. Reassigning the local slice after append does not update the caller’s header unless you return the new slice or pass a pointer to the slice.
Maps
Maps are reference types: the map value you pass behaves like a handle to shared data, so adding keys inside a function is visible to the caller. To work on an independent copy, clone the map (copy a map in Go).
package main
import "fmt"
func bumpFirst(s []int) { s[0] = 99 }
func addKey(m map[string]int) { m["k"] = 1 }
func main() {
s := []int{1, 2, 3}
bumpFirst(s)
fmt.Println(s)
m := map[string]int{}
addKey(m)
fmt.Println(m["k"])
}Locally you should see the first element become 99 and the map contain k.
Methods: value receiver versus pointer receiver
A method func (e T) M() receives a copy of T (value semantics for the receiver). A method func (e *T) M() receives the pointer value; M can mutate the pointed-to struct. Choosing between them is ordinary API design for methods, not a second calling convention for function arguments.
Summary
Go pass by value or reference is settled: arguments are always passed by value. golang pass by reference and pass by reference golang usually mean passing a pointer so the callee can mutate through it. go pass by value applies to structs and primitives as full copies. Slices and maps look like golang pass by reference or value puzzles because headers and map handles alias shared memory; only operations that replace the header (such as some append results) need an explicit return or *[]T if the caller must see the new slice.
References
- Go Tour: Pointers (structs and methods)
- Effective Go — Pointers vs. values
- Language specification — Calls

