满足接口的Go结构方法的类型

dan*_*nze 3 methods struct pointers interface go

给出以下Go代码示例:

package main

import "fmt"

type greeter interface {
    hello()
    goodbye()
}

type tourGuide struct {
    name string
}

func (t tourGuide) hello() {
    fmt.Println("Hello", t.name)
}

func (t *tourGuide) goodbye() {
    fmt.Println("Goodbye", t.name)
}

func main() {
    var t1 tourGuide = tourGuide{"James"}
    t1.hello()   // Hello James
    t1.goodbye() // Goodbye James (same as (&t1).goodbye())

    var t2 *tourGuide = &tourGuide{"Smith"}
    t2.hello()   // Hello Smith
    t2.goodbye() // Goodbye Smith (same as (*t2).hello())

    // illegal: t1 is not assignable to g1 (why?)
    // var g1 greeter = t1

    var g2 greeter = t2
    g2.hello()   // Hello Smith
    g2.goodbye() // Goodbye Smith
}
Run Code Online (Sandbox Code Playgroud)

我可以tourGuide使用tourGuide类型的变量t1或指向tourGuide的指针来调用struct的两个方法t2.换句话说,我可以T使用类型T或变量的变量调用接收器的方法*T.类似地,我可以*T使用类型的变量T(如果T可寻址的)或者调用接收器的方法*T.我知道编译器在这里处理差异(参见我在代码中的注释).

但是,当我们实现接口时,事情会发生变化.在上面的代码中,类型greeter接口的变量可以从指针分配,tourGuide但不能从a指定tourGuide.

谁能告诉我为什么会这样呢?为什么我能够打电话t1.hello(),t1.goodbye()但不知何故t1还不够接口greeter

icz*_*cza 7

如果方法具有指针接收器,则只能将指针值用作接收器值.因此,要在某个值上调用此方法,值本身必须是指针,或者必须可以获取其地址(用作接收器).

例如,如果你有一个变量,它是可寻址的,因此可以获得它的地址并将其用作接收器.规范允许你这样做,这会自动发生.

包含在接口中的值不可寻址.创建接口值时,将复制包含在接口中的值.因此无法取其地址.理论上你可以允许获取副本的地址,但这将是(甚至)更混乱的来源,而不是它提供的好处,因为地址指向副本,而带有指针接收器的方法只能修改副本而不是原始的.

请参阅此答案,详细说明/证明在创建接口值时复制值:切片如何包含自身?