Go的指针类型有一个约定:任一指针具有其指带的实体(最常见的是结构体,当然也可以是字符串、数字或者其他指针等)的所有属性、方法

这样“智能”的设定使得我们可以使用p.X代表(p).X来访问p的字段X,或者p.fn()代替(p).fn()来调用p的方法fn

而Go的接口要求结构体实现接口,当“需要实现接口”和“Go的智能推断”这两件事情相遇时,奇妙的事情就发生了🙃

假设现在有一个接口i和一个实现了它的类型str

type (
    i interface {
        val()
    }
    str string
)

func (s str) val() { // 接收者为值类型
    fmt.Println(s)
}

func main() {
    var s str = "str"
    var iVal i = s // 可行
    iVal.val()

    p := &s
    var iInter = p // 可行
    iInter.val()
}

这段代码可以正常运行,因为Go将“智能地”使s的指针类型具有s自身所具有的所有方法,那么它自然就能够被i接口变量所指带

但是反之则不行:

type (
    i interface {
        val()
    }
    str string
)

func (s *str) val() { // 接收者为指针
    fmt.Println(s)
}

func main() {
    var s str = "str"
    p := &s
    var iInter = p // 可行
    iInter.val()

    var iVal i = s // 不行!编译错误
    iVal.val()
}

因为此时类型str并没有实现方法val,而其指针类型实现的方法并不能如同上面那样被“智能地”视为值类型也实现了同样方法,所以此时无法使用接口i指带类型str的变量

Go中存在许多类似的“智能”设定,比如切片增加元素时有可能会,也可能不会对其他指向同一底层数组的切片产生影响等,如果没有深入了解这些底层机制,那么在开发过程中就容易产生难以排查的bug