
在 go 中,当通过嵌入(embedding)让子结构体获得父结构体的方法时,需注意方法是否有返回值;若误将无返回值的方法(如 `setname()`)直接传给 `fmt.println()`,会触发编译错误,因其“无值可传递”。
Go 的结构体嵌入(embedding)是一种实现代码复用和“类似继承”行为的重要机制。当你将 Foo 嵌入 Bar 时(type Bar struct { Foo; id string }),Bar 实例(尤其是指针 *Bar)会自动获得 *Foo 类型定义的所有指针接收者方法(如 (*Foo).SetName 和 (*Foo).Name),前提是这些方法的接收者是 *Foo —— 这正是本例中能成功调用 bar.Name() 的原因。
但关键区别在于方法签名:
- func (f *Foo) Name() string 返回 string,因此 bar.Name() 是一个可求值的表达式,可安全用于 fmt.Println(bar.Name());
- func (f *Foo) SetName(name string) 返回 void(即无返回值),因此 bar.SetName("...") 是一条语句,而非表达式,不能作为函数参数传递。
你遇到的错误:
./struct-2.go:33: bar.Foo.SetName("New value set to Foo struct name") used as value本质上是 Go 编译器在提示:你试图把一条没有返回值的语句当作一个“值”来使用(此处传给了 fmt.Println 的可变参数列表),这在 Go 中是非法的。
✅ 正确写法应将方法调用与打印分离:
bar.SetName("New value set to Foo struct name") // 单独调用,不参与打印
fmt.Println("Bar getName(): ", bar.Name()) // 再读取并打印⚠️ 补充注意事项:
- 嵌入仅提升方法可见性,不改变方法语义:bar.SetName(...) 实际等价于 bar.Foo.SetName(...),操作的是嵌入字段 bar.Foo 的内存;
- 若 SetName 改为值接收者 func (f Foo) SetName(...),则 bar.SetName(...) 将无法编译(因为嵌入提升仅适用于指针接收者作用于 *Bar);
- fmt.Println 接收 interface{} 类型的可变参数,它要求每个实参都必须是可求值的表达式(如变量、字面量、有返回值的函数/方法调用),而空方法调用不符合此要求。
总结:在 Go 中,切勿将无返回值的方法调用直接嵌入到需要值的上下文(如函数参数、赋值右值、if 条件等)。养成“先调用,再使用”的习惯,既符合语言规范,也提升代码可读性与健壮性。










