DEV Community

Chidozie C. Okafor
Chidozie C. Okafor

Posted on • Originally published at doziestar.Medium on

Learn It Once: “Golang Pointers are Powerful”

Pointers are a fundamental concept in Go that allow you to pass references to values and records within your program. They are a key feature of the language that can be used to improve the performance of your code and enable more powerful abstractions.

In Go, a pointer is a variable that stores the memory address of another variable. You can create a pointer by using the & operator to get the address of a variable. For example:

package main

import "fmt"

func main() {
    x := 10
    var p *int = &x

    fmt.Println(p) // prints the memory address of x
    fmt.Println(*p) // prints the value of x
}
Enter fullscreen mode Exit fullscreen mode

In this example, we create a variable x with the value 10 and a pointer p to x. The & operator returns the memory address of x, which is stored in p.

You can use the * operator to dereference a pointer and access the value it points to. In the example above, *p returns the value of x, which is 10.

Pointers can be used to pass references to values between functions. For example.

package main

import "fmt"

func addOne(x *int) {
    *x = *x + 1
}

func main() {
    x := 10
    addOne(&x)
    fmt.Println(x) // prints 11
}
Enter fullscreen mode Exit fullscreen mode

In this example, we pass the memory address of x to the addOne function as a pointer. Inside the function, we use the * operator to dereference the pointer and modify the value of x. When we print x in the main function, it has been incremented by one.

Let’s look at how to do some advance stuffs using pointers

  1. Pointer receivers in methods: In Go, you can define methods on any type, including pointers. When you define a method with a pointer receiver, it means that the method can modify the value of the receiver. For example:
package main

import "fmt"

type MyStruct struct {
    x int
}

func (s *MyStruct) increment() {
    s.x++
}

func main() {
    s := MyStruct{x: 10}
    s.increment()
    fmt.Println(s.x) // prints 11
}
Enter fullscreen mode Exit fullscreen mode

In this example, we define a method increment on MyStruct with a pointer receiver. When we call the method on an instance of MyStruct, it increments the value of x. If we had defined the method with a value receiver (e.g. func (s MyStruct) increment()), it would not have been able to modify the value of s.

  1. Pointer types: In Go, you can define a new type that is a pointer to another type. For example:
package main

import "fmt"

type MyInt int
type MyIntPointer *MyInt

func main() {
    var x MyInt = 10
    var p MyIntPointer = &x
    fmt.Println(*p) // prints 10
}
Enter fullscreen mode Exit fullscreen mode

In this example, we define a new type MyIntPointer that is a pointer to MyInt. We can then use MyIntPointer like any other pointer type.

  1. Pointers to structs: You can use pointers to structs to avoid copying large structs when you pass them as function arguments. For example:
package main

import "fmt"

type MyStruct struct {
    x int
    y int
}

func updateStruct(s *MyStruct) {
    s.x++
    s.y++
}

func main() {
    s := MyStruct{x: 10, y: 20}
    updateStruct(&s)
    fmt.Println(s) // prints {11 20}
}
Enter fullscreen mode Exit fullscreen mode

In this example, we pass a pointer to s to the updateStruct function. This avoids copying the entire struct, which can be more efficient for large structs.

Pointers can also be used to share data between goroutines, which are Go’s lightweight threads of execution. By passing a pointer to a goroutine, you can access and modify the same data from multiple goroutines.

here is an example on to use it

package main

import (
    "fmt"
    "sync"
    "time"
)

type MyStruct struct {
    x int
    y int
}

func updateStruct(s *MyStruct, wg *sync.WaitGroup) {
    s.x++
    s.y++
    wg.Done()
}

func main() {
    s := MyStruct{x: 10, y: 20}
    var wg sync.WaitGroup

    wg.Add(2)
    go updateStruct(&s, &wg)
    go updateStruct(&s, &wg)
    wg.Wait()

    fmt.Println(s) // prints {12 22}
}
Enter fullscreen mode Exit fullscreen mode

In this example, we create a struct MyStruct with two fields, x and y. We then create two goroutines that both increment x and y using a pointer to the struct. We use a sync.WaitGroup to ensure that both goroutines have finished before we print the final value of s.

Note that we pass a pointer to s and the sync.WaitGroup to each goroutine. This allows the goroutines to access and modify the same data. If we had passed the values of s and the sync.WaitGroup instead of pointers, the goroutines would have worked on copies of the data and the original values would not have been modified.

It’s worth noting that Go has a garbage collector that automatically frees memory when it is no longer needed. This means that you don’t have to worry about manually freeing memory when you are done with it, as you do in languages like C.

Overall, pointers are a powerful and important feature of Go that enable you to write efficient and expressive code. I hope this article has helped you understand how pointers work in Go.

Top comments (2)

Collapse
 
iarro profile image
Jaroslav Bulava

Thanks for great summary.

Just one note. There is typo in example "Pointers to structs". Correct result is {11 21}

Collapse
 
doziestar profile image
Chidozie C. Okafor

Thank you for pointing this out, I will check it out