Golang Tutorials

Update Structure's Data

There are now three previous golang structures guides which can be found here.

Below is the code example of a structure called "person" which contains a second structure "details".

Once we created a person, the problem here is we want to update a value on that person. So, we demonstrate the ability to update their age and print the results. First, there are a few hurdles we will have to address, namely understanding how golang works with pass by reference and pass by value.

package main

import "fmt"

// Person structure
type person struct {
	ID      int
	details details
}

// Details structure
type details struct {
	firstName string
	lastName  string
	age       int
	address   string
}

func main() {

}

There is no black magic here. A simple struct in a struct.


Next we are going to add a functions and struct specific function to our new structure

func createPerson() person {
	return person{
		ID: 1,
		details: details{
			firstName: "House",
			lastName:  "Cat",
			age:       34,
			address:   "Detroit Rock City",
		},
	}
}

func (p person) printPerson(addDetails bool) {
	if addDetails {
		fmt.Printf("%+v\n", p) // \n indicates a newline
	} else {
		fmt.Printf("%v\n", p)
	}
}

Adding a small break for our new methods for clarity!

// Non-persisted data update example
func (p person) updateAge(ageUpdate int) {
	fmt.Printf("\nChanging age from %v to %v\n", p.details.age, ageUpdate)
	// this will update the local copy of p.
	p.details.age = ageUpdate
	fmt.Printf("\nNew age is %v\n", p.details.age)
	// when you leave this method and return to main
	// this change will naturally and correctly be lost!
}

// Persisted data update example
func (p *person) realUpdateAge(ageUpdate int) {
	fmt.Printf("\nChanging age from %v to %v\n", (*p).details.age, ageUpdate)
	(*p).details.age = ageUpdate
	fmt.Printf("\nNew age is %v\n", p.details.age)
}

// Persisted data update example
func (p *person) realAlternativeUpdateAge(ageUpdate int) {
	fmt.Printf("\nChanging age from %v to %v\n", p.details.age, ageUpdate)
	p.details.age = ageUpdate
	fmt.Printf("\nNew age is %v\n", p.details.age)
}

The new take away here are the new function receiver examples.

Using (p *person), we aren't passing a value copy of type person who called the function. Instead we are passing in a value of the pointer pointing to a type of person.


Now let's call our new methods from main() and observe the outputs.

func main() {
	newperson := createPerson()
	fmt.Println("New Person Details:")
	newperson.printPerson(true)

	newperson.updateAge(35)
	fmt.Println("\nUpdated Person Details:")
	newperson.printPerson(true)

	// As you can see the real age is still 34, despite being
	// changed inside the updateAge.
	// those familiar with C or C++ will immediately know
	// that we are dealing with a common issue resolved by
	// using items known as pointers/references.

	// Golang is primarily a pass by value language.
	// Declare a variable that is a pointer, that points to the memory
	// address of newperson.
	// key syntax is the &
	pointer := &newperson

	// Update via pointer demo
	pointer.realUpdateAge(35)
	fmt.Println("\nReal Updated Person Details:")
	newperson.printPerson(true)

	// Updated via pointer but with alternative (simpler) syntax
	pointer.realAlternativeUpdateAge(36)
	fmt.Println("\nReal Alternative Updated Person Details:")
	newperson.printPerson(true)

	// Update via pointer receiver, off of the original struct with simple syntax
	newperson.realAlternativeUpdateAge(37)
	fmt.Println("\nReal Alternative Updated Person Details Without The Original Pointer:")
	newperson.printPerson(true)
}

What we have here is a demonstration of class pass by value/pass by reference problems. Pass by value means the values are lost when the scope moves on. Pass by reference means that you are dealing with a pointer, pointing to a spot in memory (usually a value or structure).

The first one, newperson.updateAge(35), demonstrates the pass by value to the receiver. It has a fully functional copy of newperson to play with locally, but thats all you can do with it, process on or with the local copy.

The second one, pointer.realUpdateAge(35), demonstrates a classic pointer to memory location, calling a function from that address, and passing that address to the receiver. Changes here are all made to the memory address that the local pointer points.

The third one, pointer.realAlternativeUpdateAge(36), is demonstrating the same thing as the second one, but with a relaxed syntax internally.

The fourth one, newperson.realAlternativeUpdateAge(37), demonstrates that you can call a pointer based function from the original variable as you will pass a pointer of the variable into the receiver of the function, allowing changes to be made in memory back to the original variable calling the function.

Sample Output

New Person Details:
{ID:1 details:{firstName:House lastName:Cat age:34 address:Detroit Rock City}}

Changing age from 34 to 35

New age is 35

Updated Person Details:
{ID:1 details:{firstName:House lastName:Cat age:34 address:Detroit Rock City}}

Changing age from 34 to 35

New age is 35

Real Updated Person Details:
{ID:1 details:{firstName:House lastName:Cat age:35 address:Detroit Rock City}}

Changing age from 35 to 36

New age is 36

Real Alternative Updated Person Details:
{ID:1 details:{firstName:House lastName:Cat age:36 address:Detroit Rock City}}

Changing age from 36 to 37

New age is 37

Real Alternative Updated Person Details Without The Original Pointer:
{ID:1 details:{firstName:House lastName:Cat age:37 address:Detroit Rock City}}

Additional Links


Everything Together

package main

import "fmt"

// Person structure
type person struct {
	ID      int
	details details
}

// Details structure
type details struct {
	firstName string
	lastName  string
	age       int
	address   string
}

func main() {
	newperson := createPerson()
	fmt.Println("New Person Details:")
	newperson.printPerson(true)

	newperson.updateAge(35)
	fmt.Println("\nUpdated Person Details:")
	newperson.printPerson(true)

	// As you can see the real age is still 34, despite being
	// changed inside the updateAge.
	// those familiar with C or C++ will immediately know
	// that we are dealing with a common issue resolved by
	// using items known as pointers/references.

	// Golang is primarily a pass by value language.
	// Declare a variable that is a pointer, that points to the memory
	// address of newperson.
	// key syntax is the &
	pointer := &newperson

	// Update via pointer demo
	pointer.realUpdateAge(35)
	fmt.Println("\nReal Updated Person Details:")
	newperson.printPerson(true)

	// Updated via pointer but with alternative (simpler) syntax
	pointer.realAlternativeUpdateAge(36)
	fmt.Println("\nReal Alternative Updated Person Details:")
	newperson.printPerson(true)

	// Update via pointer receiver, off of the original struct with simple syntax
	newperson.realAlternativeUpdateAge(37)
	fmt.Println("\nReal Alternative Updated Person Details Without The Original Pointer:")
	newperson.printPerson(true)
}

func createPerson() person {
	return person{
		ID: 1,
		details: details{
			firstName: "House",
			lastName:  "Cat",
			age:       34,
			address:   "Detroit Rock City",
		},
	}
}

func (p person) printPerson(addDetails bool) {
	if addDetails {
		fmt.Printf("%+v\n", p) // \n indicates a newline
	} else {
		fmt.Printf("%v\n", p)
	}
}

// Non-persisted data update example
func (p person) updateAge(ageUpdate int) {
	fmt.Printf("\nChanging age from %v to %v\n", p.details.age, ageUpdate)
	// this will update the local copy of p.
	p.details.age = ageUpdate
	fmt.Printf("\nNew age is %v\n", p.details.age)
	// when leave this method and return to main
	// this change will be lost!
}

// Persisted data update example
func (p *person) realUpdateAge(ageUpdate int) {
	fmt.Printf("\nChanging age from %v to %v\n", (*p).details.age, ageUpdate)
	(*p).details.age = ageUpdate
	fmt.Printf("\nNew age is %v\n", p.details.age)
}

// Persisted data update example
func (p *person) realAlternativeUpdateAge(ageUpdate int) {
	fmt.Printf("\nChanging age from %v to %v\n", p.details.age, ageUpdate)
	p.details.age = ageUpdate
	fmt.Printf("\nNew age is %v\n", p.details.age)
}


Play.Golang.Org Sandbox

I have embedded this source location for you to run and see outputs.