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的指针传递。
根据上面小节,只要用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就可以作为参数传入
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)