为什么接口不能使用指针引用来实现方法,而直接访问仍然是可能的呢?

huangapple go评论76阅读模式
英文:

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"}将会复制指针(即地址)um,这就是为什么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)

playground

英文:

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)

playground

huangapple
  • 本文由 发表于 2016年11月27日 19:00:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/40828156.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定