如何正确通过指针参数在 Go 函数中初始化并返回对象引用

12次阅读

如何正确通过指针参数在 Go 函数中初始化并返回对象引用

go 中函数参数按值传递,即使传入指针,也只是该指针值的副本;若需在函数内修改原始指针变量本身(如使其指向新分配的对象),必须传入指向指针的指针(即 `**t`),并在函数内解引用赋值。

go 语言中,所有函数参数都是按值传递的——这意味着当你把一个变量(包括指针)传给函数时,函数接收到的是该值的一个副本。这与 C/c++ 中“传指针可修改原值”的直觉类似,但有一个关键区别Go 中的指针本身也是值。因此,func f(p *T) 接收的是 *T 类型值的副本,对 p 的重新赋值(如 p = &x)只会影响副本,不会影响调用方的原始指针变量。

回到你的代码问题:

var session *mgo.Session ConnectToMongo(session) // 传入的是 nil 指针的副本

此时 ConnectToMongo 内部的 session 参数只是原始 session 变量的一个拷贝。即使你在函数内执行 session, err = mgo.Dial(…),也只是改变了这个局部副本的值,调用结束后,main 中的 session 依然为 nil。

✅ 正确做法是:使用双指针(**mgo.Session),让函数能修改原始指针变量所存储的地址:

func ConnectToMongo(session **mgo.Session) {     if *session == nil { // 注意:这里检查的是 *session,而非 session         var err error         *session, err = mgo.Dial("localhost:27028") // ✅ 解引用后赋值到原始变量         if err != nil {             panic(err)         }     } }  func main() {     var session *mgo.Session     ConnectToMongo(&session) // 传入 session 变量的地址(即 **mgo.Session)     if session == nil {         fmt.Println("nil. Why?") // 不会执行     } else {         fmt.Println("Connected successfully.") // ✅ 此时 session 已被正确初始化     } }

⚠️ 关键细节提醒:

  • if session == nil 在 **T 函数中检查的是指针的指针是否为空(即 &session 是否为 nil),而我们要判断的是目标指针是否为空,所以应写 if *session == nil;
  • 调用时务必使用 &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() // 记得关闭连接     fmt.Println("Connected.") }

这种返回式设计更清晰、更易测试、更符合 Go 的惯用法(Explicit is better than implicit),应优先采用。仅在需要复用已有变量或满足特定接口约束时,才考虑双指针方案。

text=ZqhQzanResources