
本文详解 go 语言中跨包访问与赋值全局变量的正确方式,重点解决因误用短变量 declaration `:=` 导致的编译错误,并提供线程安全、可维护的数据库连接初始化实践。
在 Go 中,跨包共享状态(例如 MongoDB 的全局 *mgo.Session)是常见需求,但必须严格遵循 Go 的变量作用域与声明规则。你遇到的错误:
./server.go:28: cannot declare name dbutil.MySession
根本原因在于::= 是短变量声明操作符,仅用于在同一作用域内创建并初始化新变量;它不能用于给已存在的包级变量赋值,更不允许带包名前缀(如 dbutil.MySession)进行声明。
✅ 正确做法是分两步完成:
- 声明局部变量 err(因为 ConnectDb() 返回两个值,需接收错误);
- 使用普通赋值 = 给导出的包级变量 dbutil.MySession 赋值。
修改 server.go 中的 main() 函数如下:
func main() {
var err error
dbutil.MySession, err = dbutil.ConnectDb() // ✅ 使用 = 赋值,不加 :=
if err != nil {
log.Fatal("Failed to connect to MongoDB:", err)
}
defer dbutil.MySession.Close() // 确保程序退出前释放资源
// 启动 HTTP 服务
http.HandleFunc("/users", getUsersHandler)
http.HandleFunc("/posts", getPostsHandler)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}⚠️ 重要注意事项:
- dbutil.MySession 必须是导出标识符:即首字母大写(MySession),且 dbutil 包需正确导入(推荐使用模块路径而非相对路径,如 "yourproject/mylib");
- 避免竞态(Race Condition):若多个 goroutine 同时读写 MySession,需加锁或改用连接池(现代推荐使用 mongo-go-driver 替代已归档的 mgo);
- 初始化时机与生命周期管理:应在 main() 中尽早初始化,并通过 defer 或 os.Exit 前显式关闭;生产环境建议封装为 InitDB() 函数并返回错误,提升可测试性;
- 替代方案更佳实践:推荐使用依赖注入(如将 *mgo.Session 作为参数传入 handler)或单例模式(配合 sync.Once 实现懒加载),避免隐式全局状态。
示例:使用 sync.Once 安全初始化(推荐)
// 在 dbutil.go 中
var (
once sync.Once
session *mgo.Session
initErr error
)
func GetSession() (*mgo.Session, error) {
once.Do(func() {
session, initErr = ConnectDb()
})
return session, initErr
}这样既避免了 main() 中手动赋值的耦合,又保证了线程安全与按需初始化。总结:Go 不支持“带包名的短声明”,跨包赋值请始终使用 =,并优先采用显式、可控、可测试的状态管理方式。










