写点什么

Dig101-Go 之如何在函数内修改指针

用户头像
newbmiao
关注
发布于: 2020 年 05 月 23 日
Dig101-Go之如何在函数内修改指针

Dig101: dig more, simplified more and know more


今天来看一个小问题:如何在函数内部修改一个指针(参数或接收者),使其值的改变能反映在函数外部


直接上代码,这样可以么?


type ArgType struct {  A string  b int}
func modifyPointerArg1(arg *ArgType) { arg = &ArgType{"arg1", 1} fmt.Println("inside modifyPointerArg1:", arg)}
复制代码


答案是【不可以】


等会分析,再看一个,这个呢?


func modifyPointerArg2(arg *ArgType) {  *arg = ArgType{"arg2", 2}  fmt.Println("inside modifyPointerArg2:", arg)}
复制代码


答案是【可以】


仔细看下,你应该就明白了。


第一个替换的是指针变量本身,


也就是在函数modifyPointerArg1的作用域内,其修改是有效


函数返回后,并不影响指针arg所指向的值(别忘了,Go 参数传递是值传递嘛!)


至于modifyPointerArg2则是对指针解引用,修改了其指向的值




这样的方式其实还有很多,比如这个:


func modifyPointerArg3(arg *ArgType) {  val := reflect.ValueOf(arg)  val.Elem().FieldByName("A").SetString("arg3")  fmt.Println("inside modifyPointerArg3:", arg)  // val.Elem().FieldByName("b").SetInt(3)  // panic: reflect: reflect.flag.mustBeAssignable using value obtained using unexported field}
复制代码


实际是利用反射的Elem()获取val的值


  • 如果其为空接口(empty interface),则获取其内部值(空接口值字段的类型是指针哦)

  • 如果其为指针(pointer),则获取其指向的值


获取到的结构如下


type Value struct {    // 类型    typ *rtype    // 值指针    ptr unsafe.Pointer    // 标志位  flag}
复制代码


然后对应类型修改时, 实际就是对指针解引用修改其指向的值


func (v Value) SetString(x string) {  v.mustBeAssignable()  v.mustBe(String)  // 这里  *(*string)(v.ptr) = x}
复制代码


再如:


func modifyPointerArg4(arg *ArgType) {  jsonStr := `{"A":"arg4","b":4}`  json.Unmarshal([]byte(jsonStr), arg)}
复制代码


内部实际也是用了反射修改指针指向的值


另外,把上边几个测试函数由指针参数换为指针接受者,也是一样的


比如:


func (arg *ArgType) modifyPointerReceiver4() {  jsonStr := `{"A":"arg4","b":4}`  json.Unmarshal([]byte(jsonStr), arg)}
复制代码


有兴趣可以去自行尝试下其他几个函数。


说了这么多,这样修改有啥用么?


常见的一个场景便是:


测试时,可以通过对接口实现对应的 mock 函数,改变参数或接收者,以达到排除依赖,进行单元测试的目的。




欢迎关注公众号:newbmiao,获取及时更新文章


推荐阅读:Dig101-Go系列,挖一挖技术背后的故事。


发布于: 2020 年 05 月 23 日阅读数: 51
用户头像

newbmiao

关注

搬砖工,投资客,奶爸 2019.05.22 加入

golang爱好者,公众号:newbmiao,博客: blog.newbmiao.com

评论

发布
暂无评论
Dig101-Go之如何在函数内修改指针