在使用mgo驱动操作mongodb时,若为结构体错误地实现了调用自身递归反序列化的`setbson`方法,将导致无限递归并触发栈溢出(stack overflow)——这是典型的接口实现陷阱。
当你为 Session 类型显式实现 SetBSON 方法,并在其中直接调用 raw.Unmarshal(rcv) 时,就无意中触发了 mgo 的递归调用链:
✅ 正确做法是:仅在需要自定义反序列逻辑时才实现 SetBSON,且绝不可在其中递归调用 Unmarshal 到同一类型。对于绝大多数场景(如本例),完全无需实现 SetBSON —— mgo 默认的结构体字段映射已足够健壮。
以下是修复后的精简示例(移除危险的 SetBSON 实现)

package main
import (
"fmt"
"os"
"gopkg.in/mgo.v2" // 注意:推荐使用新导入路径(原 labix.org/v2/mgo 已弃用)
"gopkg.in/mgo.v2/bson"
)
type Session struct {
Id bson.ObjectId `bson:"_id"`
Data map[string]interface{} `bson:"data"`
}
type Authen struct {
Name string `bson:"name"`
Email string `bson:"email"`
}
func main() {
uri := "mongodb://localhost:27017"
sess, err := mgo.Dial(uri)
if err != nil {
fmt.Printf("Can't connect to MongoDB: %v\n", err)
os.Exit(1)
}
defer sess.Close()
collection := sess.DB("test").C("sess")
a := &Authen{Name: "Cormier", Email: "cormier@example.com"}
s := &Session{
Id: bson.NewObjectId(),
Data: map[string]interface{}{"logged": a},
}
if err = collection.Insert(s); err != nil {
fmt.Printf("Insert failed: %v\n", err)
os.Exit(1)
}
var result Session
if err = collection.Find(bson.M{}).One(&result); err != nil {
fmt.Printf("Query failed: %v\n", err)
os.Exit(1)
}
fmt.Printf("Retrieved: %+v\n", result)
}⚠️ 补充注意事项:
总结:接口实现需敬畏调用契约。Setter 不是“让我自己解自己”,而是“让我控制如何被解”——控制权在你,但递归出口必须由你明确切断。