我们几乎不需要使用指向接口的指针,应该将接口直接赋值给接口,这样在传递过程中,实质上传递的底层数据仍然是指针,即不存在值拷贝的情况。
type Foo struct {
T string
}
func bar(i interface{}) {
...
}
var foo interface{} = Foo{...}
// Bad
bar(&foo)
// Good
bar(foo)
为什么可以这样,因为接口实质上在底层用两个字段表示:
(1)一个指向某些特定类型信息的指针。
(2)一个指向具体数据的指针。如果存储的数据是指针,则直接存储。如果是值,则存储指向该值的指针。
具体可以看下 Go 源码 runtime 包两种接口类型的定义。
一种是带有一组方法的接口runtime.iface
:
type iface struct {
tab *itab
data unsafe.Pointer
}
一种是不含任何方法的空接口runtime.eface
:
type eface struct {
_type *_type
data unsafe.Pointer
}
我们可以看下接口变量的内存宽度。
var foo interface{} = Foo{...}
fmt.Println(unsafe.Sizeof(foo)) // 16
当然,凡事无绝对。如果需要修改接口变量本身,那么应该使用指向接口变量的指针,当然你必须清楚自己在干什么。
类似地,在了解 map、slice、channel 的底层结构后,我们应该知道在传递过程中一般也不需要使用指向它们的指针。