skip to content
Alvin Lucillo

Data semantics

/ 2 min read

💻 Tech

In Go, there are two data semantics: value and pointer semantics.

Value Semantics

Everything in Go is passed by value. This means that when you pass a literal or a variable to a function, the function receives a copy of the value. Changes made to this copy do not affect the original value. For example:

package main

import "fmt"

func main() {
    var x int
    fmt.Println(x) // Output: 0
    func1(x)
    fmt.Println(x) // Output: 0
}

func func1(x int) {
    x++
}

In the above example, when x is passed to func1, the function gets its own copy of x. Incrementing x inside func1 does not change the original x in main.

Pointer Semantics

To allow a function to modify the original variable, you pass a pointer to the variable rather than the value itself. This is called pointer semantics. By passing a pointer, you allow the function to operate on the original data. Here’s how you can modify the previous example to use pointer semantics:

package main

import "fmt"

func main() {
    var x int
    fmt.Println(x) // Output: 0
    func1(&x)
    fmt.Println(x) // Output: 1
}

func func1(x *int) {
    (*x)++
}

In this example, func1 takes a pointer to an integer (*int). Inside func1, we dereference the pointer (*x) to increment the original x from main.

Stack and Program Boundaries

When a goroutine transitions to another function call (a program boundary), it carries a stack frame where the values are accessed and manipulated. Each function call creates a new stack frame, and when the function returns, the stack frame is destroyed, and any changes to local variables within that stack frame are lost. This is why changes to the copy of a variable in a function do not affect the original variable outside the function when using value semantics.