
本文详解 go 语言中通过匿名字段(嵌入结构体)继承父结构体方法时,为何 `bar.setname()` 不能直接作为 `fmt.println()` 的参数——根本原因在于该方法无返回值,而 `println` 需要可打印的值。
在 Go 中,嵌入(embedding)是一种实现“组合式继承”的机制:当结构体 Bar 匿名嵌入 Foo 时,Bar 实例会自动获得 Foo 类型定义的所有可导出字段和方法(前提是接收者类型兼容)。这正是 bar.Name() 能成功调用的原因——Name() 方法有返回值 string,可被 fmt.Println() 安全接收。
但问题出在这一行:
fmt.Println("Bar setName(): ", bar.SetName("New value set to Foo struct name"))SetName 的签名是:
func (f *Foo) SetName(name string) { /* no return */ }它是一个无返回值(void)方法。在 Go 中,调用一个无返回值的函数或方法,其表达式本身不产生任何可求值的结果,因此不能作为参数传递给 fmt.Println() —— 编译器报错 used as value 正是对此语义错误的精准提示。
✅ 正确写法应将方法调用与打印分离:
bar.SetName("New value set to Foo struct name") // 单独调用,执行副作用(修改状态)
fmt.Println("Bar setName(): OK") // 单独打印确认
fmt.Println("Bar getName(): ", bar.Name()) // 此时 Name() 返回 string,可打印⚠️ 注意事项:
- 嵌入仅提升方法可见性,不改变方法签名;SetName 仍属于 *Foo,但 *Bar 可代理调用(因 Bar 内含 Foo 字段且 bar.Foo 可寻址)。
- 若 Bar 同时定义了同名方法(如 SetName),则会覆盖嵌入方法,此时调用的是 Bar 自己的方法。
- 接收者必须可寻址:bar 是指针(&Bar{}),因此 bar.Foo 是可寻址的 Foo 实例,能正确绑定 *Foo 方法。若使用值类型 bar := Bar{...},则 bar.SetName(...) 将因 bar.Foo 不可寻址而编译失败(除非 SetName 接收者为 Foo 而非 *Foo)。
? 总结:Go 中方法是否可“作为值使用”,完全取决于其是否声明返回值。切勿将 void 方法误作表达式参与计算或传参。理解 embedding 的代理机制与方法调用语义,是写出健壮 Go 组合代码的关键基础。










