What's the difference between passing in a value or passing in an address into an interface in Go?

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

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语言中,一切都是按值传递的。

指针是按值传递的-使用指针的副本。

切片是按值传递的-复制底层数组的指针,长度和容量的整数值。

接口也是按值传递的-使用接口值的副本。

如果你这样考虑会有所帮助。

现在,让我们专注于接口:

接口值由两部分组成:

  1. 指向接口表的指针
  2. 指向实际值的指针

因此,当你传递一个接口时,实际上是复制了两个指针。

现在,要使类型满足接口,它需要具有接口所需的方法。如果任何方法使用指针接收器(例如你的示例中的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:

  1. A pointer to an interface table
  2. 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.

huangapple
  • 本文由 发表于 2021年10月8日 04:26:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/69487449.html
匿名

发表评论

匿名网友

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

确定