包级变量必须用var声明且首字母大写才能跨包访问;:=仅限函数内使用,包顶层用会报syntax error;小写变量不可导出,init()适合复杂初始化,并发访问需同步机制。

必须用 var 声明,不能用 :=,且首字母大写才可跨包访问。
全局变量只能用 var,不能用 :=
在函数外(即包顶层)声明变量时,:= 语法根本不可用——Go 编译器会直接报错 syntax error: non-declaration statement outside function body。这是语法硬限制,不是风格建议。
-
var GlobalConfig string = "prod"✅ 合法 -
var GlobalConfig = "prod"✅ 类型可推导,也合法 -
GlobalConfig := "prod"❌ 编译失败 -
var GlobalConfig; GlobalConfig = "prod"❌ 不允许分两行赋值(虽不报错但变量为零值,逻辑错误)
跨包使用必须首字母大写
Go 的导出规则是:只有首字母大写的标识符才能被其他包引用。小写开头的包级变量只在当前包内可见。
-
var ServerAddr string = "localhost:8080"✅ 其他包可通过yourpkg.ServerAddr访问 -
var serverAddr string = "localhost:8080"❌ 外部包无法访问,编译时报cannot refer to unexported name yourpkg.serverAddr
注意:这不是命名习惯问题,而是 Go 的语言机制。即使加了注释或文档,小写变量对外就是“不存在”。
立即学习“go语言免费学习笔记(深入)”;
初始化顺序与 init() 函数的关系
包级变量按源码中出现顺序初始化;若依赖复杂逻辑(如读配置、连接数据库),应改用 init() 函数处理,避免变量声明时隐式调用未就绪的依赖。
- 直接初始化:
var Timeout = time.Second * 30—— 安全,纯值表达式 - 危险写法:
var DB = connectDB(os.Getenv("DSN"))—— 若connectDB依赖尚未初始化的全局变量或环境,可能 panic - 推荐替代:
var DB *sql.DB; func init() { DB = connectDB(...) }
并发读写必须加锁或用原子操作
全局变量天然跨 goroutine 可见,但 Go 不保证并发安全。没有同步措施的读写大概率导致数据竞争(data race)。
- 简单计数器:
var Counter int→ 并发自增需用sync/atomic.AddInt64(&Counter, 1)或包裹sync.Mutex - 映射类结构:
var ConfigMap = make(map[string]string)→ 必须配sync.RWMutex,否则运行时报fatal error: concurrent map writes - 工具链检测:
go run -race main.go能暴露大部分竞态问题
真正难的不是声明,而是后续谁在什么时候改它、是否线程安全、有没有循环依赖——这些不会在 var 那行报错,但会在上线后深夜报警。


















