英文:
Why interface don't implement method with pointer reference while direct access is still possible?
问题
我理解接口在Go规范和常见问题解答中不允许使用带有指针引用的方法,因为T和*T具有不同的方法集(https://golang.org/doc/faq#guarantee_satisfies_interface)。
所以,下面的代码是无法工作的:
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)
}
并且返回以下错误信息:
> ./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)
这个错误的解释是:
>[…]在方法调用中,没有一种有用的方式可以获得指针。
>
>即使在编译器可以获取值的地址并将其传递给方法的情况下,如果方法修改了该值,更改也会在调用者中丢失。
然而,将interfaceModify(u)
替换为直接调用u.modify()
是可以工作的:编译器会获取u的地址,并且会修改它的name属性,Println()函数也证实了这一点。
因此,在这种特定情况下,我们可以执行这个操作,但在接口中却不能。我对这种差异的唯一解释是,在interfaceModify(m modifier)
中,我们将直接将u的副本复制给m,而在调用modify()
时,m无法匹配相应的地址。
因此,声明u := &user{"Default Name"}
将会复制指针(即地址)u给m,这就是为什么m.modify()
是可行的原因。
我理解得对吗?
英文:
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?
答案1
得分: 5
我认为你已经理解了。u.modify()
之所以有效,是因为Go将其视为(&u).modify()
的简写形式。interfaceModify(&u)
也可以工作。这里有一个Playground,其中包含一些关于按引用传递和按值传递的更多示例。
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)
}
英文:
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)
}
答案2
得分: 2
T
类型不是 &T
类型。换句话说,指向结构体的类型指针不是结构体类型。因此,如果你决定使用指针接收器来实现一个方法,你必须使用指针而不是结构体本身,因为只有指针才能实现该方法。你的代码只缺少一个字符。
将 interfaceModify(u)
改为 interfaceModify(&u)
英文:
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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论