包级变量只能用var声明,不能用:=;后者仅限函数内使用,且需至少一个新变量,否则报“no new variables”错误。

包级变量只能用 var,写 name := "x" 直接报错
Go 不允许在函数外使用 :=。一旦你在 main 函数之外、甚至只是在文件顶部写 power := 9000,编译器会立刻报错:non-declaration statement outside function body。
包级变量必须显式用 var 声明,哪怕你有初始值:
package main
var power int = 9000 // ✅ 合法
var name = "Goku" // ✅ 合法,类型由字面量推导
var (
level int = 1
mode string
) // ✅ 批量声明,mode 得到零值 ""
- 导出变量(首字母大写)必须用
var,否则无法被其他包引用 - 需要显式指定底层类型时(如
type UserID int64),uid := 123会推导成int64,丢失类型语义;正确写法是var uid UserID = 123 - 想让变量在包内全局可见且可被多个文件访问,只能靠
var声明的包级变量
:= 要求至少一个新变量,重复用会触发 no new variables on left side of :=
这不是语法限制,而是 Go 编译器对“声明”行为的严格界定::= 是声明 + 赋值,不是单纯赋值。所以 power := 9000 后再写 power := 9001,编译器一看:左边没新变量,拒绝执行。
但你可以混用已有变量和新变量,只要左侧至少有一个新名字:
func main() {
power := 9000
name, power := "Goku", 9001 // ✅ 合法:name 是新变量,power 重赋值
fmt.Println(name, power) // 输出 "Goku 9001"
}
- 常见坑:在
if或for块里用:=声明变量,结果块外访问不到 —— 因为作用域仅限该块,不是“变量未声明”,而是“根本不可见” - 多变量赋值时,
a, b := 1, "x"中若a已存在,b是新的,整行仍合法;但如果a和b都已存在,就报错 - 别指望用
:=给结构体字段或 map 元素“声明”,它只作用于局部变量
类型推导看似方便,但在 slice、map、interface 上容易掉坑
v := []string{"a"} 没问题,m := map[string]int{"k": 1} 也没问题,但推导结果未必是你真正想要的类型。
-
s := []int{}得到的是非 nil 切片,s := []int(nil)得到的是 nil 切片 —— 两者len()都是 0,但后者调用append(s, 1)会 panic,前者不会 -
r := strings.NewReader("x")推导出*strings.Reader,不是io.Reader;虽然能传给接受io.Reader的函数,但后续想调r.Size()就会编译失败 - 字面量
42推导为平台相关int(通常是int64),但如果你的结构体字段是int32,直接field := 42再赋值过去会报错,得写field := int32(42)
零值不是“空”,而是语言强制保证的安全起点
Go 不允许变量处于未初始化状态。声明即赋予零值:int 是 0,string 是 "",bool 是 false,map 和 slice 是 nil。
关键在于:nil 不等于空,而是“未分配”。直接对 nil slice 或 map 进行操作会 panic:
var users []User
users[0] = User{} // panic: index out of range
var config map[string]string
config["timeout"] = "30" // panic: assignment to entry in nil map
- 切片要初始化为
make([]User, 0)或[]User{}(后者创建非 nil 空切片) - map 必须用
make(map[string]string)或字面量map[string]string{},不能依赖零值 - 结构体字段如果是指针、slice、map、channel、func、interface,零值是 nil,需显式初始化才可安全使用
最易被忽略的是:零值本身是确定的、可预测的,但 nil 值的行为和非 nil 值完全不同,不加区分地当成“空”来用,是 runtime panic 的主要来源之一。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











