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