Invalid Memory Address or Nil Pointer Dereference
Lastmod: 2023-01-26

Overview

Example error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x497783]

This issue happens when you reference an unassigned (nil) pointer.

Go uses an asterisk * to specify a pointer to a variable, and an ampersand & to generate a pointer to a given variable. For example:

  • var p *int - p is a pointer to a type integer
  • p := &i - p is a pointer to variable i

Initial Steps Overview

  1. Check if the pointer is being set

  2. Check for a nil assignment to the pointer

Detailed Steps

1) Check if the pointer is being set

Before a pointer can be referenced, it needs to have something assigned to it.

type person struct {
	Name string
	Age  int
}

func main() {
	var myPointer *person
	fmt.Println(myPointer.Name)
}
$ go run main.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x497783]

The above code causes Go to panic because, while myPointer has been declared as a pointer to a person object, it currently does not point to a particular instance of such an object. This can be solved by assigning a variable to the pointer (Solution A) or by creating and referencing a new object (Solution B).

2) Check for a nil assignment to the pointer

Another possibility is that the pointer is being set to nil somewhere in your code. This could be, for example, a function returning nil after failing to accomplish a task.

type NumberObject struct {
	number int
}

func createNumberObj(num int) (result *NumberObject) {
	// Returns a pointer to a NumberObject if the number is allowed, otherwise returns nil
	if num < 100 {
		numberObj := NumberObject{num}
		return &numberObj // Returns a reference to the new object
	} else {
		return nil
	}
}

func main() {
	myNumberObject := createNumberObject(101)
	fmt.Println(myNumberObject.number) // This will cause Go to panic!
}

Here a NumberObject is created only if the input is less than 100. A pointer is returned by the function either way, but the main() function is not prepared to handle the nil pointer that is returned if the conditions aren’t met, causing Go to panic. It would be best to handle this issue by either handling the nil pointer as demonstrated in Solution B, or by creaing and referencing an empty object as per Solution C.

Solutions List

A) Assign a variable to the pointer

B) Handle the nil pointer

C) Create and reference a new variable

Solutions Detail

A) Assign a variable to the pointer

type Person struct{
	Name string
	Age int
}

func main()
	var myPointer *Person // This is currently a nil pointer

	aPerson := Person{
		Name: "Joey",
		Age:  29,
	} // This is a Person object

	myPointer = &aPerson // Now the pointer references the same Person as 'aPerson'

	fmt.Println(joey.Name)
}

Here the variable aPerson is created, after which we can use the Go & syntax to get this variable’s reference (location in memory) and assign it to myPointer. Importantly, both myPointer and aPerson now point to the same variable in memory, and modifications made to either will apply to both.

func main()
	var myPointer *person
	myPointer = &anotherPerson

	anotherPerson.Name = "Pete"

	fmt.Println(myPointer.Name) // This will print "Pete"!
}

B) Handle the nil pointer

Going back to the code used in Step 2, we can expand this to check for and ‘handle’ a nil value before the code continues.

type NumberObject struct {
	number int
}

func createNumberObj(num int) (result *NumberObject) {
	// Returns a pointer to a NumberObject if the number is allowed, otherwise returns nil
	if num < 100 {
		numberObj := NumberObject{num}
		return &numberObj // Returns a reference to the new object
	} else {
		return nil
	}
}

func main() {
	myNumberObject := createNumberObject(101)

	if myNumberObject == nil {
		log.Fatal("Failed to create number object!")
	}

	fmt.Println(myNumberObject.number) // This line is not reached if num >= 100!
}

There are several ways this situation could be handled; in this particular instance the log.Fatal function will terminate the program if myNumberObject is a nil pointer. Another option would be to return an error in the createNumberObj function informing the user that the inputted number was too high. A further option, and one which would allow the program to continute executing, would be to create and reference a new variable, as seen in Solution C.

C) Create and reference a new variable

type NumberObject struct {
	number int
}

func createNumberObj(num int) (result *NumberObject) {
	numberObj := NumberObject{} // Creates a new, empty NumberObject
	if num < 100 {
		numberObj.number = num
	}
	return &numberObj // Returns a reference to the new object
}

func main() {
	myNumberObject := createNumberObject(101)

	fmt.Println(myNumberObject.number) // Will print 0 instead of causing a panic!
}

Here we have restructured the program to first create a new variable, and then edit the properties of this object as the program executes, removing the risk of the function returning a nil pointer. In the event it is not explicitly set, the value of NumberObject.number will default to 0, rather than nil as you might expect - this is because Go has default ‘zero values’ for all it’s types, which you can read more about here.

Further Information

Gotcha Nil Pointer Dereference

Golang default zero values

Go Pointers

Owner

Joey

comments powered by Disqus