Golang方法使用指针接收器

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

Golang method with pointer receiver

问题

我有这个示例代码:

package main

import (
	"fmt"
)

type IFace interface {
	SetSomeField(newValue string)
	GetSomeField() string
}

type Implementation struct {
	someField string
}

func (i Implementation) GetSomeField() string {
	return i.someField
}

func (i Implementation) SetSomeField(newValue string) {
	i.someField = newValue
}

func Create() IFace {
	obj := Implementation{someField: "Hello"}
	return obj // <= 有问题的行
}

func main() {
	a := Create()
	a.SetSomeField("World")
	fmt.Println(a.GetSomeField())
}

SetSomeField 方法不按预期工作,因为它的接收者不是指针类型。

如果我将该方法更改为指针接收者,我期望它能正常工作,代码如下:

func (i *Implementation) SetSomeField(newValue string) { ...

编译时会出现以下错误:

prog.go:26: cannot use obj (type Implementation) as type IFace in return argument:
	Implementation does not implement IFace (GetSomeField method has pointer receiver)

如何使 struct 实现接口 并且 SetSomeField 方法能够修改实际实例的值而不创建副本?

这是一个可修改的代码片段:
https://play.golang.org/p/ghW0mk0IuU

我已经看过这个问题 https://stackoverflow.com/questions/29221854/in-go-golang-how-can-you-cast-an-interface-pointer-into-a-struct-pointer,但我看不出它与这个示例有什么关系。

英文:

I have this example code

package main

import (
	&quot;fmt&quot;
)

type IFace interface {
	SetSomeField(newValue string)
	GetSomeField() string
}

type Implementation struct {
	someField string
}

func (i Implementation) GetSomeField() string {
	return i.someField
}

func (i Implementation) SetSomeField(newValue string) {
	i.someField = newValue
}

func Create() IFace {
	obj := Implementation{someField: &quot;Hello&quot;}
	return obj // &lt;= Offending line
}

func main() {
	a := Create()
	a.SetSomeField(&quot;World&quot;)
	fmt.Println(a.GetSomeField())
}

SetSomeField does not work as expected because its receiver is not of pointer type.

If I change the method to a pointer receiver, what I would expect to work, it looks like this:

func (i *Implementation) SetSomeField(newValue string) { ...

Compiling this leads to the following error:

prog.go:26: cannot use obj (type Implementation) as type IFace in return argument:
	Implementation does not implement IFace (GetSomeField method has pointer receiver)

How can I have the struct implement the interface and the method SetSomeField change the value of the actual instance without creating a copy?

Here's a hackable snippet:
https://play.golang.org/p/ghW0mk0IuU

I've already seen this question https://stackoverflow.com/questions/29221854/in-go-golang-how-can-you-cast-an-interface-pointer-into-a-struct-pointer, but I cannot see how it is related to this example.

答案1

得分: 82

你指向结构体的指针应该实现接口。这样你就可以修改它的字段。

看看我如何修改你的代码,使其按照你的期望工作:

package main

import (
	"fmt"
)

type IFace interface {
	SetSomeField(newValue string)
	GetSomeField() string
}

type Implementation struct {
	someField string
}    

func (i *Implementation) GetSomeField() string {
	return i.someField
}

func (i *Implementation) SetSomeField(newValue string) {
	i.someField = newValue
}

func Create() *Implementation {
	return &Implementation{someField: "Hello"}
}

func main() {
	var a IFace
	a = Create()
	a.SetSomeField("World")
	fmt.Println(a.GetSomeField())
}
英文:

Your pointer to the struct should implement the Interface. In that way you can modify its fields.

Look at how I modified your code, to make it working as you expect:

package main

import (
	&quot;fmt&quot;
)

type IFace interface {
    SetSomeField(newValue string)
	GetSomeField() string
}

type Implementation struct {
	someField string
}    

func (i *Implementation) GetSomeField() string {
	return i.someField
}

func (i *Implementation) SetSomeField(newValue string) {
	i.someField = newValue
}

func Create() *Implementation {
	return &amp;Implementation{someField: &quot;Hello&quot;}
}

func main() {
	var a IFace
	a = Create()
	a.SetSomeField(&quot;World&quot;)
	fmt.Println(a.GetSomeField())
}

答案2

得分: 40

简单的答案是,你无法使结构体实现你的接口,并且让SetSomeField按照你的期望工作。

然而,结构体的指针可以实现接口,所以将你的Create方法改为return &obj应该可以解决问题。

根本问题在于,你修改后的SetSomeField方法不再是Implementation的方法集。虽然类型*Implementation会继承非指针接收器的方法,但反过来则不成立。

这是因为接口变量的指定方式与此有关:访问接口变量中存储的动态值的唯一方式是复制它。举个例子,想象一下以下情况:

var impl Implementation
var iface IFace = &impl

在这种情况下,调用iface.SetSomeField可以工作,因为它可以复制指针并将其用作方法调用中的接收器。如果我们直接将结构体存储在接口变量中,我们需要创建一个指向该结构体的指针来完成方法调用。一旦创建了这样的指针,就可以访问(并可能修改)接口变量的动态值,而无需复制它。

英文:

The simple answer is that you won't be able to have the struct implement your interface while having SetSomeField work the way you want.

However, a pointer to the struct will implement the interface, so changing your Create method to do return &amp;obj should get things working.

The underlying problem is that your modified SetSomeField method is no longer in the method set of Implementation. While the type *Implementation will inherit the non-pointer receiver methods, the reverse is not true.

The reason for this is related to the way interface variables are specified: the only way to access the dynamic value stored in an interface variable is to copy it. As an example, imagine the following:

var impl Implementation
var iface IFace = &amp;impl

In this case, a call to iface.SetSomeField works because it can copy the pointer to use as the receiver in the method call. If we directly stored a struct in the interface variable, we'd need to create a pointer to that struct to complete the method call. Once such a pointer is made, it is possible to access (and potentially modify) the interface variable's dynamic value without copying it.

huangapple
  • 本文由 发表于 2015年11月26日 18:23:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/33936081.html
匿名

发表评论

匿名网友

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

确定