Golang中如何模拟database/sql的Rows.Scan实现字符串赋值?
为什么你的MockScan无法修改外部字符串变量?
这是一个典型的Go语言值传递特性导致的误解问题,咱们一步步拆解清楚:
首先看你的代码核心问题:当你调用MockScan(&s1)时,args[0]是&s1的副本——Go里所有参数都是值传递,哪怕是指针也不例外。你在函数里做args[0] = &s2,只是把这个指针副本改成了指向s2,但完全没碰原来的&s1指向的内存区域,所以外部的s1自然不会有任何变化。
而database/sql的Rows.Scan之所以能修改外部变量,核心逻辑是它不会修改指针参数本身,而是通过指针间接修改其指向的内存。举个例子,对于传入的字符串指针,它会把数据库查询到的字符串值,写入到这个指针指向的地址里(也就是外部变量的内存位置),而不是替换掉指针。
修正后的MockScan实现
我们来写一个能正确模拟Scan行为的版本:
package main import ( "fmt" "log" ) func MockScan(args ...interface{}) error { // 模拟从数据库获取的测试值 mockStr := "模拟的数据库字符串" for idx, dest := range args { // 类型断言判断传入的是不是字符串指针 strPtr, ok := dest.(*string) if !ok { return fmt.Errorf("索引%d处的参数不是*string类型", idx) } // 解引用指针,修改它指向的内存区域(也就是外部的s1变量) *strPtr = mockStr } return nil } func main() { var s1 string fmt.Println("Scan前的s1值:", s1) if err := MockScan(&s1); err != nil { log.Fatal(err) } fmt.Println("Scan后的s1值:", s1) }
运行这个代码,你会看到s1成功被修改成了模拟的字符串值。
再深入理解值传递
回到你原来的运行结果:
0xc000012950 // main里的&s1地址 0xc000012950 // MockScan里args[0]的初始值(&s1的副本,地址相同) 0xc000012960 // args[0]被赋值为&s2后的地址(指向s2的内存) 0xc000012950 // main里的&s1还是原来的地址,完全没被修改
这里的关键是:args[0]是指针的副本,修改它本身不会影响外部的&s1。只有当你解引用这个副本(*args[0]),修改它指向的内容,才能让外部变量s1发生变化——因为这个副本和原来的&s1指向的是同一块内存(也就是s1的存储位置)。
总结核心要点
- Go语言中所有参数都是值传递,包括指针类型,传递的是指针的副本
Rows.Scan的核心是通过指针的副本,间接修改外部变量的内存,而不是修改指针本身- 你的MockScan错误地修改了指针副本的指向,而不是通过指针修改目标变量的内容
内容的提问来源于stack exchange,提问作者natsuki_2002
相关产品推荐
相关产品推荐

