go reflect 与slice

1. slice

slice底层存储的其实一个结构,如下:

type slice struct {
        array unsafe.Pointer
        len   int
        cap   int
}

先提供一篇文章加深理解slice对象的文章:

https://segmentfault.com/a/1190000017910165?utm_source=sf-related

以下我用go1.13.8 linux/amd64,再次测试:
func testsliceInt(sliceInt []int) {
    fmt.Printf("testslice: &sliceInt addr:%p\n", &sliceInt)
    fmt.Printf("testslice: sliceInt array addr:%p, val:%v\n", sliceInt, sliceInt)
    sliceInt[0] = 11
    fmt.Printf("testslice: sliceInt array addr:%p, val:%v\n", sliceInt, sliceInt)
    sliceInt = append(sliceInt, 12)
    fmt.Printf("testslice: sliceInt array addr:%p, val:%v\n", sliceInt, sliceInt)
}

func main(){
    sliceInt := make([]int, 1, 2)
    sliceInt[0] = 10
    fmt.Printf("&sliceInt addr:%p\n", &sliceInt)
    length := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&sliceInt)) + uintptr(8)))
    cap := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&sliceInt)) + uintptr(16)))
    fmt.Printf("sliceInt.len val:%v\n", length)
    fmt.Printf("sliceInt.cap val:%v\n", cap)


    fmt.Printf("sliceInt array addr:%p, val:%v\n", sliceInt, sliceInt)
    testsliceInt(sliceInt)
    fmt.Printf("sliceInt array addr:%p, val:%v\n", sliceInt, sliceInt)
}
输出:
&sliceInt addr:0xc00000c220
sliceInt.len val:1
sliceInt.cap val:2
sliceInt array addr:0xc000018090, val:[10]
testslice: &sliceInt addr:0xc00000c280
testslice: sliceInt array addr:0xc000018090, val:[10]
testslice: sliceInt array addr:0xc000018090, val:[11]
testslice: sliceInt array addr:0xc000018090, val:[11 12]
sliceInt array addr:0xc000018090, val:[11]

       与前面给出的文档实验一致的是,go 函数slice的传递其实是值传递,所以只传递了底层数组的地址,所以对底层数组的内容的修改可以传回去。但是不同的地方是现在只要append的值都传不回了,即使还没扩容。

        但是不影响结论,在函数内只要涉及到append必须用slice的指针传递。

2.根据给定类型生成对应类型的slice指针

       根据上面小节,只要用slice对象传递且对其append都必须传递slice指针。这里列出个实际场景例子:

//golang mongo driver: 
collection.Find(bson.M{}).All(result) //这里的result就必须传递slice指针,api里参数interface{}

   现有Test结构体如下:

type Test struct{
    Name string
}

通常用户可以直接调用 var result *[]Test

但是如果通过中间件进行转化时,具体结构类型就是未知的,这时可以用reflect函数来生成:

 var value interface{} = Test{}
    sliceType := reflect.SliceOf(reflect.TypeOf(value))
    slice:= reflect.MakeSlice(sliceType, 0, 0)
    slicedata := reflect.New(slice.Type())
    slicedata.Elem().Set(slice)
    data :=slicedata.Interface()

此时data就可以作为参数传入

3.模拟mongo处理未知类型的slice指针

func dealsliceptr(value interface{}) {
    val := reflect.ValueOf(value)
    ind := reflect.Indirect(val)
    tmpslice := ind
    tmpslice = reflect.Append(tmpslice, reflect.ValueOf(Test{Name: "test"}))
    ind.Set(tmpslice)

猜你喜欢

转载自blog.csdn.net/yshhuan/article/details/110469992