Go使用MongoDB驱动时操作bson.M为何必须做类型断言?
Go mongo-driver 中bson.M操作的类型断言问题
问题描述
使用go.mongodb.org/mongo-driver库开发时编写了如下代码:
updateMap := bson.M{ "$set": bson.M{ "dateAdded": primitive.NewDateTimeFromTime(time.Now()), }, } if len(alias) > 0 { // 我不理解此处类型断言的必要性 updateMap["$set"].(map[string]interface{})["alias"] = alias }
我不理解上述代码中对updateMap["$set"]做(map[string]interface{})类型断言的必要性。如果尝试编写如下代码:
updateMap["$addFields"].(map[string]map[string]string)["alias"] = alias
代码会编译失败。
我已知bson.M的本质类型是map[string]interface{},编译器也知晓updateMap的类型为bson.M,为何断言其为map[string]interface{}是编译通过的必要操作?我原本认为编译器需要明确接口值的实际具体类型,但实际表现和我的预期不符。
如果此处不添加类型断言,代码会直接报编译错误:
非法操作:无法索引
updateMap["$addFields"](map索引操作的目标类型为interface{},不支持索引)
(原始报错:invalid operation: cannot index updateMap["$addFields"] (map index expression of type interface{}))
原因说明
核心原因是Go是静态类型语言,编译阶段只会按照变量的静态类型做校验,不会自动推断接口值承载的动态类型:
- 首先明确
bson.M的定义是type M map[string]interface{},也就是说无论你往这个map里存入什么类型的值,通过键索引拿到的返回值,静态类型永远是interface{}。编译器在编译时不会追踪你存入值的实际类型,只认这个静态声明的类型。 - Go语法不允许直接对
interface{}类型的值做索引操作,这就是不加类型断言时报错的直接原因:编译器看到updateMap["$set"]的类型是interface{},根本不知道它是个可以用键索引的map,自然不允许你后面写["alias"]取值赋值。 - 对
updateMap["$set"]断言为map[string]interface{}能编译通过、运行正常,是因为你初始化时存入"$set"键的实际值就是bson.M,而bson.M的底层类型正好是map[string]interface{},类型断言匹配,自然可以正常执行。 - 你写的
updateMap["$addFields"].(map[string]map[string]string)会失败有两个原因:一是初始化时你根本没有给updateMap添加"$addFields"这个键,取到的值是nil;二是就算你提前存入了bson.M类型的值,它的动态类型是map[string]interface{},和map[string]map[string]string是完全不同的类型,类型断言不可能匹配,要么编译报错要么运行时panic。
更稳妥的写法
不建议在链式操作里反复做类型断言,很容易触发panic,可以提前把要修改的文档存为独立变量,操作完再放入更新map:
setClause := bson.M{ "dateAdded": primitive.NewDateTimeFromTime(time.Now()), } if len(alias) > 0 { setClause["alias"] = alias } updateMap := bson.M{ "$set": setClause, }
内容的提问来源于stack exchange,提问作者jacob
相关产品推荐
相关产品推荐

