
本文讲解 Go 语言中通过结构体嵌入(embedding)实现接口时,如何正确将源结构体赋值给目标结构体的嵌入字段,并解释为何直接赋值 *dest = theOrigin 会编译失败。
本文讲解 go 语言中通过结构体嵌入(embedding)实现接口时,如何正确将源结构体赋值给目标结构体的嵌入字段,并解释为何直接赋值 `*dest = theorigin` 会编译失败。
在 Go 中,destination 并非 origin 的别名或同类型结构体,而是一个独立类型,仅在其定义中嵌入了 origin 字段:
type destination struct {
origin // 嵌入字段,等价于匿名字段 origin origin
}
该嵌入语法会在 destination 中自动声明一个名为 origin 的字段(类型为 origin),并提升其方法(若存在)。但 destination 与 origin 是完全不同的类型,因此以下赋值非法:
*dest = theOrigin // ❌ 编译错误:cannot use theOrigin (type origin) as type destination
Go 是强类型语言,不允许跨类型直接赋值,即使二者字段完全一致——这是类型安全的设计原则。
✅ 正确做法是显式地将 theOrigin 赋值给嵌入字段:
func (dest *destination) LaunchTheDevice(theOrigin origin) {
dest.origin = theOrigin // ✅ 合法:为嵌入字段 origin 赋值
}
此时 dest.origin 是 destination 结构体中一个类型为 origin 的字段,赋值自然成立。
? 补充说明:你之所以能写 dest.name = "a value",是因为 Go 自动“提升”了嵌入字段 origin 的导出字段(name、value、infos 均首字母大写才可导出;注意原文中字段均为小写,实际需修正为 Name, Value, Infos 才能外部访问)。但字段提升不改变类型关系,它只是语法糖,底层仍是 dest.origin.Name = ...。
✅ 完整可运行示例(已修正字段导出性及初始化):
package main
import "fmt"
type intf interface {
SaySomething(string)
LaunchTheDevice(origin)
}
type destination struct {
origin
}
func (dest *destination) SaySomething(s string) {
fmt.Println("I'm saying --> ", s)
}
func (dest *destination) LaunchTheDevice(theOrigin origin) {
dest.origin = theOrigin // 关键修正:赋值给嵌入字段
}
type origin struct {
Name, Value, Infos string // ✅ 改为首字母大写以支持外部访问和字段提升
}
func main() {
firstValue := origin{
Name: "Nyan",
Value: "I'm the only one",
Infos: "I'm a cat",
}
secondValue := &destination{} // 使用 &destination{} 更清晰(等价于 new(destination))
secondValue.LaunchTheDevice(firstValue)
// 验证字段已正确复制(得益于字段提升)
fmt.Printf("Name: %s, Value: %s, Infos: %s\n",
secondValue.Name, secondValue.Value, secondValue.Infos)
// 输出:Name: Nyan, Value: I'm the only one, Infos: I'm a cat
}
⚠️ 注意事项:
- 若
origin字段名冲突(例如destination本身也定义了Name),则字段提升失效,必须显式通过dest.origin.Name访问; - 嵌入是组合(composition)而非继承(inheritance),
destination不是origin的子类,二者无类型兼容性; - 若需深度拷贝含指针或复杂嵌套结构,应考虑使用
reflect或专用库(如copier),但本例为纯值类型,直接赋值即可。
总结:Go 中结构体嵌入不产生类型等价性,赋值必须明确作用于嵌入字段本身(dest.origin = ...),而非整个接收者(*dest = ...)。理解这一区别,是写出类型安全、语义清晰 Go 代码的关键。











