Pointers In Go

Kinanee Samson - Nov 3 '22 - - Dev Community

Pointers In Go

Pointers are objects that store a memory address. A pointer references a location in memory. A pointer is a simple, more concrete implementation of the more abstract reference data type. The term pointer applies to any data structures whose interface explicitly allows the pointer to be manipulated as a memory address. Several languages, especially low-level languages, support some type of pointer. Attempting to dereference a pointer whose value is not a valid memory address could cause a program to crash.

Concept Of Pointers

You have your house obviously which you live in, now there are times when people would want to know where you live, it makes no sense to always just take them outright to your house straight away, there are times when people want to know where you live but do not want to visit you. So on that occasion, you provide your address, and now your house address points to the location of your house.

If you are a beginner in Go then you'd better check this introduction to Go

Let's create a pointer

package main

import "fmt"

func main() void {
  i := 10
  p := &i

  fmt.PrintLn(i)
  fmt.PrintLn(p)
  fmt.PrintLn(*p)
}

// 10
// 0xc000040250
// 10

Enter fullscreen mode Exit fullscreen mode

To read more on Go at Netcreed

In the code snippet above, we create a variable of type int, then we create a pointer p that stores the memory address of i. when we want to get the memory address of a variable, we use the & operator, observing our use of type inference here. So what the heck is a memory address of a variable? Variables are labels that are used to store a value, each value is stored in a memory location and this memory location is known as the memory address on your computer ram while your code is running. The name of the variable is a tag to the value stored inside the address. A pointer such one expressed above is a reference to a memory location. let's see how they work with strings too

package main

import "fmt"

func main() void {
  i := "sam"
  p := &i

  fmt.PrintLn(i)
  fmt.PrintLn(p)
  fmt.PrintLn(*p)
}

// sam
// 0xc000040250
// sam

Enter fullscreen mode Exit fullscreen mode

When we are obtaining the value stored at that location we say we as dereferencing the pointer. We use * to dereference a pointer, rather than returning to us the memory address, it will return to us the underlying value which is stored at that address.

Why Use Pointers?

They are primarily used for constructing references, which in turn are fundamental to constructing nearly all data structures, as well as in passing data between different parts of a program. We use pointers to pass data to a function by reference, that is to say, we get the underlying value which is stored at the address as opposed to the variable itself, this has some interesting consequences. When we start to work with structs, you will realize that structs are not mutable, any change to a struct will only create a copy of the original with the modified changes, and the change will not take effect on the original struct. We use structs to combat situations like this.

package main

import "fmt"

type Person struct {
  name    string
  age     int
  gender  string
  hobbies []string
}

func NewPerson(_name string, age int) Person {
  p := Person{
    name:    _name,
    age:     0,
    hobbies: []string{},
  }
  return p
}

func addHobby(hobby string, p Person) {
  p.hobbies = append(p.hobbies, hobby)
}

Enter fullscreen mode Exit fullscreen mode

We have created a simple struct that serves as the blueprint for a Person object, Structs like we said are quite immutable to changes, we will run the above code and observer that the hobby we try to append to the hobbies array gets ignored.

func main() {
  sam := NewPerson("sam", 30)
  fmt.Println(sam)
  addHobby("singing", sam)
  fmt.Println(sam)
}

// {sam 30  []} before


// {sam 30  []} after
Enter fullscreen mode Exit fullscreen mode

Now let's modify the addHobby function slightly, instead of accepting a variable of type Person we will only accept a pointer to a Person

func addHobby(hobby string, p *Person) {
  p.hobbies = append(p.hobbies, hobby)
}


func main() {
  sam := NewPerson("sam", 30)
  fmt.Println(sam)
  and hobby("singing", &sam)
  fmt.Println(sam)
}

// {sam 30  []} before


// {sam 30  [singing]} after

Enter fullscreen mode Exit fullscreen mode

Should You use Pointers?

A common idea is that when you use pointers your application will be faster because you’ll avoid copying data around all the time. When Java came around, one of the complaints was that Java was slow because you’d be doing a pass-by-value all the time. Hence perhaps it’s no surprise that in Go we find the same idea persisting. Yet, Passing pointers in Go are often slower than passing values. This is a result of Go being a garbage-collected language. this post explains more on the subject.

That's it for this one, if you found it useful you can read more on Go at Netcreed

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .