Go中:=是短变量声明而非赋值,仅限函数内使用,且左侧变量必须为当前作用域未声明过的;重复使用或跨作用域遮蔽将导致编译错误。

Go里:=不是“赋值”,是“短变量声明”
很多人以为:=只是var x int = 1的简写,其实它有严格限制:只能在函数内部使用,且左侧变量名必须是**本次作用域中尚未声明过的**。一旦重复写x := 5(而x已存在),编译器直接报错:no new variables on left side of :=。
常见错误场景:
- 在
if块内用:=声明了err,接着在else里又写err := handle()→ 报错 - 循环中每次迭代都用
val := data[i]→ 每次都是新声明,但若循环外已有val,第一轮就失败
哪些类型能被正确推断?哪些会翻车?
Go能根据右侧表达式字面量或函数返回值准确推断基础类型、结构体、切片、map、指针等,但有几类容易出问题:
-
nil单独出现时无法推断类型,x := nil编译失败;必须显式写成var x *int = nil或x := (*int)(nil) - 数值字面量如
42默认推为int,但传给期望int64的函数时会报错,不能靠推断“自动升级” - 接口类型无法从
nil推断,r := io.Reader(nil)合法,r := nil不合法
示例:
立即学习“go语言免费学习笔记(深入)”;
str := "hello" // 推断为 string
nums := []int{1,2,3} // 推断为 []int
m := map[string]int{} // 推断为 map[string]int
p := &str // 推断为 *string
什么时候必须用var而不是:=?
三种典型情况绕不开var:
- 包级变量声明(函数外):
var ConfigPath = "/etc/app.conf",:=语法不允许出现在包作用域 - 声明但暂不赋值(零值初始化):
var buf bytes.Buffer,此时还没调用buf.Write(),用:=无法只声明不赋值 - 需要指定具体类型而非依赖推断结果:
var id uint64 = 123,避免因字面量123被推为int导致后续运算溢出或类型不匹配
嵌套作用域里:=的“隐藏重声明”陷阱
在if、for、switch块内用:=,看似声明新变量,实则可能创建同名但作用域更小的变量,导致外部变量被遮蔽:
err := doThing() // 外层 err
if err != nil {
err := handleError() // 新声明!外层err不可见,且此处err未被使用会报错
log.Println(err)
}
修复方式只有两种:
- 用
=赋值代替:=(前提是err已声明) - 换变量名,比如
err2 := handleError()
这种遮蔽不易察觉,尤其在多层嵌套+error处理密集的代码里,是真实线上bug的温床。
类型推断本身很可靠,但:=的行为边界和作用域规则比表面看起来更硬、更不容妥协。


















