We cannot update Struct instance fields in Golang! That is partly true because Golang passes arguments to function by value even when it is a struct instance. Aside from function parameters, the effect is the same for “receiver” arguments.
Sample Codes To Update Struct Fields in Golang
We will use some sample codes to show that we cannot update Struct instance fields by default. For example, we have the following codes. We have a struct and a receiver function in the codes that try to update the petOwner field with a new owner.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package main import "fmt" type Pet struct { petName string petOwner string } func (p Pet) updatePetOwner(newPetOwner string) { // petOwner will never update to whatever value newPetOwner has p.petOwner = newPetOwner } func main() { myPet := Pet{petName:"George", petOwner:"Gary"} myPet.updatePetOwner("Sarah") // Prints out {George Gary} fmt.Printf("%v", myPet) } |
When we run the codes, we get the following output. Notice the value of the struct field petOwner does not change from Gary to Sarah.
1 | {George Gary} |
Use Pointers To Modify Struct Fields’ Values
We need pointers to modify struct fields in Golang. Using pointers involves using the ampersand (&) and asterisk symbols before the variable name. When using the ampersand symbol, we get the memory address of the value a variable holds. For example, please look into the following codes.
1 2 3 4 5 6 7 | myName := "karl" fmt.Println("My Name:", myName) // We use the & symbol infront or before the myName variable memoryAddressOfMyName := &myName fmt.Println("My Name at memory location:", memoryAddressOfMyName) |
We get the following result when we run these codes: the string karl and its location in the computer memory. The same thing happens when we apply to structs and their fields.
1 2 | My Name: karl My Name at memory location: 0xc000042230 |
The memory location of the string varies between runs and even computers. Therefore, we cannot rely on the value to be some constant. If a pointer returns the memory location of the value a variable holds, how do we change the value? We use a deference operator, the asterisk (*) symbol in Golang (and other C-based programming languages).
For our Golang struct example codes, we can use the ampersand and asterisk symbols to update the struct fields. So, how do we change the codes? First, we slightly modify the definition of the updatePetOwner function.
1 2 3 | func (p *Pet) updatePetOwner(newPetOwner string) { p.petOwner = newPetOwner } |
Then, we call the function on the pointer variable.
1 2 | mutablePet := &myPet mutablePet.updatePetOwner("Sarah") |
Therefore, we have the following complete Golang codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package main import "fmt" type Pet struct { petName string petOwner string } func (p *Pet) updatePetOwner(newPetOwner string) { p.petOwner = newPetOwner } func main() { myPet := Pet{petName: "George", petOwner: "Gary"} mutablePet := &myPet mutablePet.updatePetOwner("Sarah") fmt.Printf("%v", myPet) } |
When we run these new codes, we get the following output.
1 | {George Sarah} |
Alternatively, we can skip using the ampersand (&) symbol and directly can the new function on the struct instance. However, this can be tricky and could lead to misleading codes. For example, the following codes will not change the petOwner value from Gary to Sarah.
1 2 3 4 5 6 | //... myPet := Pet{petName: "George", petOwner: "Gary"} mutablePet := myPet mutablePet.updatePetOwner("Sarah") fmt.Printf("%v", myPet) //... |
This technique is Golang’s shortcut to pointers, and the change will happen on the copy of the struct instance in the mutablePet variable, not in the myPet variable.
1 | fmt.Printf("%v", mutablePet) |
Therefore, when we print out the value of the mutablePet variable, we get the following output.
1 | {George Sarah} |