Reputation: 151
I do understand that interface don't implement method with pointer reference as per Go spec and FAQ, as T and *T have different method sets (https://golang.org/doc/faq#guarantee_satisfies_interface).
So, this doesn't work:
package main
import (
"fmt"
)
type user struct {
name string
}
type modifier interface {
modify()
}
func (u *user) modify() {
u.name = "My name"
}
func interfaceModify(m modifier) {
m.modify()
}
func main() {
u := user{"Default Name"}
interfaceModify(u)
fmt.Println(u.name)
}
and returns:
./main.go:26: cannot use u (type user) as type modifier in argument to interfaceModify: user does not implement modifier (modify method has pointer receiver)
This is explained as:
[…]there is no useful way for a method call to obtain a pointer.
Even in cases where the compiler could take the address of a value to pass to the method, if the method modifies the value the changes will be lost in the caller.
However, replacing interfaceModify(u)
with a direct call like u.modify()
does work: the compiler takes the address of u, and will modify its name property as Println() confirms.
So, we are able to do that operation in that precise case, but not in the interface one. My only explanation of that difference is that in interfaceModify(m modifier)
, we will have a direct copy of u to m, and no way for m to match corresponding address when calling modify()
.
And, so, declaring u := &user{"Default Name"}
would thus copy the pointer (so address) u to m, and that's why m.modify()
is possible.
Am I correct?
Upvotes: 4
Views: 396
Reputation: 31109
T
type is not '&T' type. In other words a type pointer to struct is not a struct type. So if you decided to implement a method with pointer receiver you must use the pointer not the struct itself because exactly pointer implements the method. Your code lacks just one character.
Change interfaceModify(u)
to interfaceModify(&u)
Upvotes: 2
Reputation: 189
I think you've got it. u.modify()
works because go sees it as shorthand for (&u).modify()
. interfaceModify(&u)
would also work. Here's a playground with some more examples of pass by reference vs. value.
https://play.golang.org/p/HCMtcFAhLe
package main
import (
"fmt"
)
type user struct {
name string
}
type userPointer struct {
user
}
func (up *userPointer) modify() {
up.name = "My name"
}
type modifier interface {
modify()
}
func (u user) modify() {
u.name = "My name"
}
func interfaceModify(m modifier) {
m.modify()
}
func main() {
u := user{"Default Name"}
u.modify()
fmt.Println(u.name)
interfaceModify(u)
fmt.Println(u.name)
up := userPointer{user{"Default Name"}}
interfaceModify(&up)
fmt.Println(up.name)
// short hand version
up.name = "Default Name"
up.modify()
fmt.Println(up.name)
// long hand version https://golang.org/ref/spec#Calls
up.name = "Default Name"
(&up).modify()
fmt.Println(up.name)
}
Upvotes: 6