Values and pointers

In Go, everything is passed by a value, so when a function or method is invoked, a copy of the variable is made in the stack. This implies that changes that are made to the value are not reflected outside of the called function. Even slices, maps, and other reference types are passed by value, but since their internal structure contains pointers, they act as if they were passed by reference. If a method is defined for a type, it cannot be defined for its pointer and vice versa. The following example has been used to check that the value is updated only inside the method, and that the change does not reflect the main function:

package main

import (
"fmt"
)

type A int

func (a A) Foo() {
a++
fmt.Println("foo", a)
}

func main() {
var a A
fmt.Println("before", a) // 0
a.Foo() // 1
fmt.Println("after", a) // 0
}

In order to change the original variable, the argument must be a pointer to the variable itself – the pointer will be copied, but it will reference the same memory area, making it possible to change its value. Note that assigning another value pointer, instead of its content, doesn't change what the original pointer refers to because it is a copy. 

If we use a method for the type instead of its pointer, we won't see the changes being propagated outside the method.

In the following example, we are using a value receiver. This makes the User value in the Birthday method a copy of the User value in main:

type User struct {
Name string
Age int
}

func (u User) Birthday() {
u.Age++
fmt.Println(u.Name, "turns", u.Age)
}

func main() {
u := User{Name: "Pietro", Age: 30}
fmt.Println(u.Name, "is now", u.Age)
u.Birthday()
fmt.Println(u.Name, "is now", u.Age)
}

The full example is available at https://play.golang.org/p/hnUldHLkFJY.

Since the change is applied to the copy, the original value is left intact, as we can see from the second print statement. If we want to change the value in the original object, we have to use the pointer receiver so that the one that's been copied will be the pointer and the changes will be made to the underlying value:

func (u *User) Birthday() {
u.Age++
fmt.Println(u.Name, "turns", u.Age)
}

The full example is available at https://play.golang.org/p/JvnaQL9R7U5.

We can see that using the pointer receiver allows us to change the underlying value, and that we can change one field of the struct or replace the whole struct itself, as shown in the following code:

func (u *User) Birthday() {
*u = User{Name: u.Name, Age: u.Age + 1}
fmt.Println(u.Name, "turns", u.Age)
}

The full example is available at https://play.golang.org/p/3ugBEZqAood.

If we try to change the value of the pointer instead of the underlying one, we will edit a new object that is not related to the one that was created in main, and the changes will not propagate:

func (u *User) Birthday() {
u = &User{Name: u.Name, Age: u.Age + 1}
fmt.Println(u.Name, "turns", u.Age)
}

The full example is available at https://play.golang.org/p/m8u2clKTqEU.

Some types in Go are automatically passed by reference. This happens because these types are defined internally as structs that contain a pointer. This creates a list of the types, along with their internal definition:

Types Internal definitions
map
struct {
m *internalHashtable
}
slice
struct {
array *internalArray
len int
cap int
}
channel
struct {
c *internalChannel
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.80.3