
go 函数参数按值传递,即使传入的是指针(如 `*mgo.session`),函数内对其赋值仅修改副本;要真正更新原始变量,必须传入指向该指针的指针(即 `**mgo.session`),并在函数内解引用赋值。
在 Go 中,所有函数参数都是按值传递的——这意味着传入函数的不是变量本身,而是它的副本。这一点对指针类型尤其容易产生误解:虽然 *T 是一个指针,但 *T 本身仍是一个值(存储着内存地址的变量),它在函数调用时也会被复制。
以原始代码为例:
func ConnectToMongo(session *mgo.Session) {
session, err = mgo.Dial("localhost:27028") // ❌ 只修改了参数副本,不影响 main 中的 session
}这里 session 是 *mgo.Session 类型的形参,其值(即 nil 地址)被复制进函数。后续 session = ... 只是让这个局部副本指向新地址,而 main 中的 session 变量依然为 nil。
✅ 正确做法是使用双重指针(**mgo.Session),让函数能修改原始指针变量的值:
func ConnectToMongo(session **mgo.Session) {
if *session == nil { // 注意:此处应检查 *session,而非 session(后者是 **mgo.Session,不可能为 nil 除非传入 nil 地址)
var err error
*session, err = mgo.Dial("localhost:27028")
if err != nil {
panic(err)
}
}
}
func main() {
var session *mgo.Session
ConnectToMongo(&session) // 传入 session 变量的地址
if session == nil {
fmt.Println("nil. Why?") // 不会执行
} else {
fmt.Println("Connected successfully.")
defer session.Close() // 记得关闭连接
}
}⚠️ 关键注意事项:
if session == nil 在 **mgo.Session 函数中是检查“指针的指针是否为空”,而非“目标指针是否为空”——逻辑错误;应改为 if *session == nil。
&session 获取的是 *mgo.Session 类型变量的地址,类型为 **mgo.Session,与函数签名严格匹配。
-
现代 Go 推荐更清晰、更符合惯用法的写法:直接返回值,而非依赖多重指针副作用:
func ConnectToMongo() (*mgo.Session, error) { return mgo.Dial("localhost:27028") } func main() { session, err := ConnectToMongo() if err != nil { panic(err) } defer session.Close() }这种方式语义明确、易于测试、避免隐式状态修改,是更健壮和可维护的设计。
总结:Go 中无法通过单级指针参数“输出”新对象地址;若必须使用参数输出模式,务必使用 **T 并正确解引用 *session = newValue;但优先推荐返回值方式,兼顾简洁性与安全性。










