英文:
What's the difference between passing in a value or passing in an address into an interface in Go?
问题
让我们假设我们有一个简单的接口和实现:
type Vertex struct {
X int
Y int
}
type Abser interface {
Abs() float64
}
func (v Vertex) Abs() float64 {
sum := float64(v.X + v.Y)
return math.Sqrt(sum)
}
现在我有一个接口变量:
var abser Abser
我想将一个顶点设置给它。我可以设置一个值,也可以设置一个地址:
v := Vertex{1, 1}
abser = v
abser = &v
这两者之间有什么区别?为什么设置一个顶点的地址可以工作?这与接口的内部工作原理有什么关系?我对Go还是很新,所以任何帮助都将不胜感激 🙏
英文:
Let's say we have a simple interface and implementation:
type Vertex struct {
X int
Y int
}
type Abser interface {
Abs() float64
}
func (v Vertex) Abs() float64 {
sum := float64(v.X + v.Y)
return math.Sqrt(sum)
}
Now I have an interface variable:
var abser Abser
I want to set a vertex to it. I am able to set the value of one, or the address of one:
v := Vertex{1, 1}
abser = v
abser = &v
What is the difference between the two? Why does setting the address of a Vertex work? How does this tie in with how interfaces work under the hood? I'm still quite new to Go so any help would be much appreciated 🙏
答案1
得分: 2
在Go类型系统中,使用值接收器(value-receiver)为类型T
定义的方法同时适用于T
和*T
,即:
func (v Vertex) Abs() float64 {...}
这对于v
和*v
都有效。方法本身始终接收v
的副本。
而使用指针接收器(pointer-receiver)为*T
定义的方法只适用于*T
,而不适用于T
,即:
func (v *Vertex) SetX(newx float64) {v.X=newx}
只有当接收器是可寻址的时候,SetX
方法才能正常工作。这是为了避免写出会丢失数据的代码。例如:
m:=map[string]Vertex{}
m["a"]=Vertex{}
m["a"].SetX(1) // 这会失败!m["a"]不可寻址
如果上述情况不会失败,那么SetX
将设置m["a"]
的副本,而更新的副本不会被放回到map中。
回到接口的讨论:你的Abser
接口被任何实现了Abs() float64
方法的类型所实现。根据上述讨论,Vertex
和*Vertex
都实现了Abser
接口。
假设你将Vertex.Abs
定义为:
func (v *Vertex) Abs() float64 {...}
那么,只有*Vertex
实现了Abser
接口,因此:
abser = v // 这会失败
abser = &v // 这会成功
英文:
In the Go type system, a method defined using a value-receiver for type T
is defined for both T
and *T
, that is:
func (v Vertex) Abs() float64 {...}
This works for both v
and *v
. The method itself always receives a copy of v
.
A method defined for *T
is only defined for *T
and not for T
. That is:
func (v *Vertex) SetX(newx float64) {v.X=newx}
The method SetX
will only work when the receiver is addressable. This is necessary so that you don't write code that loses data. For instance:
m:=map[string]Vertex{}
m["a"]=Vertex{}
m["a"].SetX(1) // This fails! m["a"] is not addressable
If the above case did not fail, then SetX
would set a copy of m["a"]
, and the effects would be lost, because the updated copy is not put back into the map.
Going back to interfaces: your Abser
interface is implemented by any type that implements Abs() float64
. Based on the above discussion, both Vertex
and *Vertex
implement Abser
.
Let's say you defined Vertex.Abs
as:
func (v *Vertex) Abs() float64 {...}
Then, only *Vertex
implements Abser
, so :
abser = v // This would fail
abser = &v // This would work
答案2
得分: 2
首先,在Go语言中,一切都是按值传递的。
指针是按值传递的-使用指针的副本。
切片是按值传递的-复制底层数组的指针,长度和容量的整数值。
接口也是按值传递的-使用接口值的副本。
如果你这样考虑会有所帮助。
现在,让我们专注于接口:
接口值由两部分组成:
- 指向接口表的指针
- 指向实际值的指针
因此,当你传递一个接口时,实际上是复制了两个指针。
现在,要使类型满足接口,它需要具有接口所需的方法。如果任何方法使用指针接收器(例如你的示例中的func (v *Vertex) Abs() float64 {...}
),那么你需要一个值的指针来满足接口,这就是为什么你需要取其地址的原因。
假设没有任何方法需要指针接收器(比如func (v Vertex) Abs() float64 {...}
),那么你就不需要指定地址,因为你可以直接传递它。
希望这回答了你的问题。
英文:
First, in Go, everything is pass by value.
Pointers are passed by value - a copy of the pointer is used.
Slices are passed by value - the pointer to the underlying array, the integer value of the length and the integer value of the capacity are copied.
Interfaces are also passed by value - a copy of the interface value is used.
It helps if you think about it this way.
Now, let's focus on interfaces:
An interface value consists of two parts:
- A pointer to an interface table
- A pointer to the actual value
So when you pass an interface, you're essentially copying two pointers.
Now, for a type to satisfy an interface, it needs to have the methods the interface requires. If any of the methods use a pointer receiver (like func (v *Vertex) Abs() float64 {...}
in your example), then you will need a pointer to the value to satisfy the interface and that is why you need to take the address of it.
Let's say none of the methods required a pointer receiver (say func (v Vertex) Abs() float64 {...}
). Then you wouldn't need to specify the address because you would just pass it without taking its address.
I hope that answers your question.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论