
当 Go 将 struct 赋值给 interface{} 时,会将该 struct 的类型信息和值副本(非指针)打包存入接口的两个字段中;开销主要来自值拷贝,而非接口本身,其成本等同于一次 struct 赋值,与 struct 大小线性相关但无额外抽象惩罚。
当 go 将 struct 赋值给 interface{} 时,会将该 struct 的**类型信息**和**值副本**(非指针)打包存入接口的两个字段中;开销主要来自值拷贝,而非接口本身,其成本等同于一次 struct 赋值,与 struct 大小线性相关但无额外抽象惩罚。
在 Go 中,interface{} 是一个空接口,其底层由两个机器字(machine word)组成:一个指向类型信息(_type)的指针,另一个指向数据值(data)的指针。当我们将一个值(如 Person{})赋给 interface{} 时,Go 会执行以下操作:
- 类型检查与记录:确定 Person 的具体类型,并将对应 runtime._type 结构体地址写入接口的类型字段;
- 值拷贝:将 Person 实例的完整内存内容(即 name string 和 age int)复制到堆或栈上的一块新内存区域,并将该地址存入接口的数据字段。
注意:此处的“拷贝”是值语义的深拷贝——不是浅拷贝指针,而是逐字段复制结构体所有字段的值(包括 string 的 header 和 data 指针,但 string 底层数据本身不重复分配)。因此,拷贝开销与 struct 的内存大小成正比,例如:
type Small struct { x int } // 8 bytes
type Large struct {
a, b, c, d [1024]int // ~32KB
}
将 Large{} 赋给 interface{} 的耗时 ≈ 直接 var copy Large = original 的耗时,接口包装本身不引入额外的动态分配或反射开销。
✅ 正确用法(无需显式转换):
func test(any interface{}) {}
func main() {
p := Person{"Alice", 30}
test(p) // ✅ 编译器自动完成 interface{} 装箱(boxing)
var v interface{} = p // ✅ 显式赋值,效果相同
}
⚠️ 性能注意事项:
- 若 struct 较大(>128B),频繁装箱可能引发可观的内存拷贝开销;
- 可考虑传递 *Person 替代 Person:test(&p) —— 此时仅拷贝 8 字节指针,且接口内存储的是指针值,类型变为 *Person;
- 接口底层存储的是值副本,修改接口内值不会影响原始 struct(除非原值本身就是指针);
- 编译器可能对小 struct 进行逃逸分析优化,但拷贝行为语义上始终存在。
总结:struct → interface{} 的本质是「类型+值」的封装,核心成本在于 struct 值的复制,而非接口抽象。它不是零成本操作,但成本透明、可预测,且与普通变量赋值一致——理解这一点,就能在性能敏感场景中做出合理设计:小结构体放心传值,大结构体优先传指针。











