Go赋值需依场景严守规则:普通变量用:=须新声明且注意类型推导;结构体赋值区分值/指针接收;反射赋值必经可寻址、CanSet、类型匹配三关。

= 是 Go 里最基础的赋值操作符,但“怎么用”得看场景:普通变量、结构体字段、指针、反射动态赋值,每种都有明确约束和常见翻车点。
普通变量赋值必须注意作用域和类型推导
函数内可用 := 快速声明并赋值,但不能在包级或 if/for 块外重复使用;包级变量只能用 var。类型推导有时会掉坑:v := 42 推出的是 int,不是 int64;m := map[string]int(nil) 类型对但值为 nil,后续写入直接 panic。
-
:=左侧至少一个变量名是新声明的,否则报错no new variables on left side of := - 接口类型推导出的是具体类型(如
*strings.Reader),不是目标接口(如io.Reader),调用接口外方法会编译失败 - 切片字面量
[]string{"a"}没问题,但s := []string(nil)后len(s)是 0,s = append(s, "x")才真正分配底层数组
结构体字段赋值要区分值接收和指针接收
结构体变量本身是值类型,直接赋值会拷贝整个结构体;想修改原结构体字段,必须传指针。方法接收者也一样:func (s Student) Name() 无法修改 s 字段,func (s *Student) SetName(n string) 才可以。
- 结构体字面量初始化时,
&Student{Name: "A"}和new(Student)效果不同:前者字段按字面量设值,后者全为零值 - 嵌入字段赋值需显式指定层级,
s.Embedded.Field = 1,不能省略Embedded - JSON 反序列化到结构体时,字段必须首字母大写(导出),否则
json.Unmarshal不会写入
reflect 动态赋值必须过三关:可寻址、可设置、类型匹配
用 reflect.Value.Set 修改字段前,不检查就 panic 是常态。核心口诀:传指针 → .Elem() → .FieldByName → .CanSet() → 按 .Kind() 选 SetString/SetInt 等。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 错误写法:
v := reflect.ValueOf(s); v.FieldByName("Name").SetString("x")→ panic,因为v不可寻址 - 正确起点:
v := reflect.ValueOf(&s).Elem(),再f := v.FieldByName("Name"),必须f.IsValid() && f.CanSet() - 对
int字段传字符串值,不能直接f.SetString("123"),得先strconv.ParseInt再f.SetInt - 字段是
*string类型时,要先f.Elem().SetString("x"),否则SetString试图给指针本身赋字符串值,类型不匹配
指针赋值不是复制地址那么简单
ptr1 := &x; ptr2 := ptr1 确实让两个指针指向同一地址,但常见误区是以为“改指针变量本身就能影响外部”。函数参数传指针,只是传了地址副本;想在函数内让调用方的指针变量指向新地址,必须传 **T。
立即学习“go语言免费学习笔记(深入)”;
-
func setPtr(p *string) { s := "new"; p = &s }—— 这不会改变调用方的指针指向,只改了函数内局部变量p - 真要改外部指针,得写
func setPtr(p **string) { s := "new"; *p = &s },调用时传&ptr - 结构体指针字段赋值时,
s.P = &v是安全的;但如果v是局部变量,函数返回后s.P就成悬垂指针(Go 编译器通常会逃逸分析阻止这种写法)
.CanSet() 检查,尤其是字段名拼错或未导出时,FieldByName 返回零值 reflect.Value,.SetXxx 直接 panic,而错误信息又不提示字段名,调试起来很绕。

















