
在 go 中,只有可寻址的变量才能作为赋值操作的左值;函数返回的结构体值(非指针)是临时、不可寻址的,因此无法直接对其字段赋值,而返回指针则可寻址并支持字段修改。
在 go 中,只有可寻址的变量才能作为赋值操作的左值;函数返回的结构体值(非指针)是临时、不可寻址的,因此无法直接对其字段赋值,而返回指针则可寻址并支持字段修改。
Go 的赋值语句要求左操作数(LHS)必须是一个可寻址的变量(addressable value),即该值在内存中有明确位置,能被取地址(&x 合法)。这是语言规范的硬性约束,目的是确保赋值具有可观测的副作用——否则修改一个瞬时存在的临时值毫无意义。
考虑如下简化示例:
package main
type Employee struct {
ID int
Salary float64
}
func EmployeeByID(id int) *Employee {
return &Employee{ID: id, Salary: 5000.0}
}
func EmployeeByIDValue(id int) Employee {
return Employee{ID: id, Salary: 5000.0}
}
func main() {
id := 123
// ✅ 合法:*Employee 是可寻址的(指向堆/栈上的实际结构体)
EmployeeByID(id).Salary = 0 // 修改生效,影响底层对象
// ❌ 编译错误:Employee 值是临时的、不可寻址的
// EmployeeByIDValue(id).Salary = 0 // compile error: cannot assign to EmployeeByIDValue(id).Salary
// ✅ 合法(但效果不同):先绑定到变量,再赋值
emp := EmployeeByIDValue(id)
emp.Salary = 0 // 修改的是 emp 的副本,原返回值已丢弃,无外部影响
}关键原因在于:
- EmployeeByID(id) 返回 *Employee → 解引用后(如 (*ptr).Salary)等价于对底层 Employee 实例的字段访问,该实例是可寻址的;
- EmployeeByIDValue(id) 返回 Employee → 表达式结果是一个临时值(temporary value),Go 规定其不可寻址(参考 Go Spec: Address operators),因此 x.f = v 中的 x.f 不构成合法左值。
⚠️ 注意事项:
- 即使结构体字段是导出的(大写开头),也不改变其可寻址性规则;
- 使用 &struct{} 字面量(如 &Employee{})返回指针是安全的,因为 Go 会自动将字面量分配到堆或延长其生命周期以保证指针有效;
- 若需通过值类型实现类似效果,必须显式绑定到局部变量(如 e := f(); e.field = v),但此时修改仅作用于副本,不会影响调用方上下文。
总结:Go 通过强制“可寻址性”保障内存安全与语义清晰——只有真正持有状态的对象(变量、切片元素、映射值(若可寻址)、解引用后的指针等)才允许字段赋值。理解这一机制,是写出符合 Go 惯用法、避免静默错误的关键。


















