我们几乎不需要使用指向接口的指针,应该将接口直接赋值给接口,这样在传递过程中,实质上传递的底层数据仍然是指针,即不存在值拷贝的情况。

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 的底层结构后,我们应该知道在传递过程中一般也不需要使用指向它们的指针。

powered by Gitbook该文章修订时间: 2024-03-22 15:30:00

results matching ""

    No results matching ""