【问题标题】:What's the difference between passing in a value or passing in an address into an interface in Go?在 Go 中传入值或将地址传入接口有什么区别?
【发布时间】:2021-11-27 21:59:58
【问题描述】:

假设我们有一个简单的接口和实现:

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

两者有什么区别?为什么设置 Vertex 的 地址 有效?这与接口如何在幕后工作有什么关系?我对 Go 还是很陌生,所以任何帮助都将不胜感激????

【问题讨论】:

  • 您可以为接口分配实现它的任何类型的值。 *Vertex 的方法集包括在非指针类型Vertex 上声明的方法,如specs 中所述。

标签: arrays go pointers interface


【解决方案1】:

在Go类型系统中,为T*T都定义了一个使用T类型的值接收器定义的方法,即:

func (v Vertex) Abs() float64 {...}

这适用于v*v。方法本身总是会收到v 的副本。

*T 定义的方法只为*T 定义,而不为T 定义。那就是:

func (v *Vertex) SetX(newx float64) {v.X=newx}

SetX 方法仅在接收器可寻址时有效。这是必要的,这样您就不会编写丢失数据的代码。例如:

m:=map[string]Vertex{}
m["a"]=Vertex{}
m["a"].SetX(1) // This fails! m["a"] is not addressable

如果上述情况没有失败,那么SetX会设置一个m["a"]的副本,并且效果会丢失,因为更新的副本没有放回地图中。

回到接口:您的Abser 接口由实现Abs() float64 的任何类型实现。基于以上讨论,Vertex*Vertex 都实现了Abser

假设您将Vertex.Abs 定义为:

func (v *Vertex) Abs() float64 {...}

那么,只有*Vertex 实现了Abser,所以:

abser = v // This would fail
abser = &v // This would work

【讨论】:

    【解决方案2】:

    首先,在 Go 中,一切都是按值传递的。

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

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

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

    如果你这样想的话会很有帮助。

    现在,让我们关注接口:

    一个接口值consists of two parts

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

    所以当你传递一个接口时,你实际上是在复制两个指针。

    现在,对于满足接口的类型,它需要具有接口所需的方法。如果任何方法使用指针接收器(如您的示例中的func (v *Vertex) Abs() float64 {...}),那么您将需要一个指向该值的指针以满足接口,这就是您需要获取它的地址的原因。

    假设没有一个方法需要指针接收器(比如func (v Vertex) Abs() float64 {...})。那么你就不需要指定地址了,因为你只是传递它而不获取它的地址。

    我希望这能回答你的问题。

    【讨论】:

    • “在 Go 中,一切都是按值传递”。 Slice 是一个引用类型,所以我认为这个结论是不正确的。阅读更多here
    • 否则这个答案很好。
    • 其实切片也是传值的。与接口一样,切片由部分组成 - 指向数据的指针、长度和容量。当您传递一个切片时,这 3 个部分被复制 - 一个指向底层数组的指针和 2 个整数,就像您链接到的页面所说的那样。
    猜你喜欢
    • 2020-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-13
    • 2019-07-06
    • 2015-12-26
    • 2017-02-17
    • 1970-01-01
    相关资源
    最近更新 更多