I am still learning go and need a hand to clear my head.
Following program output Power
value as 1
in each Println
. I was expecting 1
as first output and 2
as second output. My assumption was the Change
func
overwrite the address of
s
with anew address
and this change will reflect back to the caller (main func
). In such case, the original address would be pointing to newly created address when it call the second Println
. My assumption is wrong but I can't figure out why.
package main
import (
"fmt"
)
type Pod struct{
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
s = &Pod{2}
}
To explore further on what happens under cover, I did tried to print addresses this time and it looks like below;
import (
"fmt"
)
type Pod struct{
Power int
}
func main() {
pod := &Pod{ 1}
fmt.Println(&pod) //0xc04202c020
Change(pod)
fmt.Println(&pod) //0xc04202c020
}
func Change(s *Pod) {
fmt.Println(&s) //0xc04202c030 ( I was expecting 0xc04202c020 here)
s = &Pod{ 2}
fmt.Println(&s) //0xc04202c030
}
It's because when you pass arguments to functions etc, they are always passed by value.
In other words, even though you are accepting a pointer in the function parameter, the address of the struct will be copied.
Then when you assign to s
to try and change the address it only changes that local copy, not the one outside the function.
To change the address from within the function you would need to pass in a pointer pointer, then assign to the dereferenced s
, for example:
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(&pod)
fmt.Println(pod.Power)
}
func Change(s **Pod) {
*s = &Pod{2}
}
In this case a copy of an address is still being passed into the function, but because it's a pointer to a pointer it means that when you dereference s
as *s
you will get the address of the struct outside of the function. This means if you assign to *s
you can change the address of the pointer outside the function.
Of course, like Andy Schweig says, you probably wouldn't really want to do that though, and would probably just change individual fields as needed with the normal pointer version of the function.
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
s.Power = 2
}
This works because when you type s.Power = 2
Go will actually do something like (*s).Power = 2
for you. So it automatically dereferences s
for you which gives you the actual Pod
struct to work with.
You can't do *s = &Pod{2}
in this normal pointer example because in that case *s
will actually equal type Pod
, not *Pod
.
Because of that if you want to use the &Pod{2}
syntax to assign an address you need to pass a pointer to the pointer. In the case of s **Pod
the dereferenced *s
will point to the address of the Pod
instead of the actual Pod
, so *s
will be of type *Pod
which allows you to assign &Pod{2}
.
Having said all of that, a **Pod
is only required if you want to assign an address with the &Pod{2}
syntax.
If you don't need to use the &Pod{2}
syntax you can just dereference s
and assign with a normal Pod{2}
.
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
*s = Pod{2}
}
This also works, because now s
is a copy of an address, and when you dereference you get to the value outside the function which is type Pod
, not *Pod
.
That means you can just assign to it by removing the &
.
Basically if you use &
it means you want to assign an address rather than the actual value.
I hope this explanation isn't too confusing. I explained using a **Pod
because I thought you wanted to use the &Pod{2}
syntax rather than Pod{2}
, but if that's not the case then my s.Power = 2
or *s = Pod{2}
examples may make more sense.
Changing the value of a parameter to a function inside the function never has an effect on the parameter passed by the caller. All parameters are passed by value. If you want to change the value of Power
inside the object pointed to by s
(pod
in the caller), use s.Power = 2
. If you actually want to set the pointer variable in the caller to a different Pod
object, you need to declare the parameter to Change
as s **pos
, change the assignment in the function to *s = &Pod{2}
, and call Change(&pod)
. That's probably not what you want to do, though.