我想使用反射扩展切片的容量,但变量本身是一个
interface{}
,我需要首先获取底层类型。但是,尝试访问基础类型会导致无法扩展其容量的不可寻址值。我知道我需要传递一个指向切片的指针,因为否则不可寻址的值不会使我的更改冒泡,但是如果我传递一个指向 reflect.ValueOf
的指针,我会得到一个指针->接口->切片的情况,而我想要指针->切片,即切片的可寻址版本。
我整理了一个最小的例子来演示这个问题。
extend
函数接收 any
类型的变量,该变量实际上是一个切片(可以是任何类型的切片,例如 []any
、[]string
、[]MyStruct
)。在我的特殊情况下,我不能使用泛型,这个例子只是这里的先决条件的简化版本。
在我的示例中,我列出了我尝试过的内容,也可以在线获取:https://go.dev/play/p/LBOrORw3oSs
package main
import (
"fmt"
"reflect"
)
func extend(v any) (any, error) {
// however, on array set, we can test if the value is maybe an array:
rv := reflect.ValueOf(v)
rt := rv.Type()
kind := rt.Kind()
if kind != reflect.Slice {
return nil, fmt.Errorf("value is not a slice, but rather %T", v)
}
c := rv.Cap()
c = c*2 + 1
// this panics because rv is not addressable
rv.SetCap(c)
// in consequence, this panics as well, because rv is not addressable
rv.Addr().SetCap(c)
// I tried getting a pointer to the value
ptr := &v
rvPtr := reflect.ValueOf(ptr)
rvPtr.SetCap(c) // panics because kind is Pointer
rvPtr.Elem().SetCap(c) // panics because kind is Interface
// calling elem twice gives me the correct kind (slice) because now I got the underlying type,
// but now we have the same situation as before: unaddressable value
rvPtr.Elem().Elem().SetCap(c)
// have to return here because we extend the capacity of a slice that is not passed as pointer
return rv.Interface(), nil
}
func main() {
slice := []any{}
result, err := extend(slice)
fmt.Println(err)
fmt.Println(cap(result.([]any)))
}
slices.Grow
,您可以使用 reflect
包执行相同的操作。
使用
make
和copy
扩展切片容量的方法直接转换为通过reflect
可用的功能。您分配一个具有正确类型、长度和容量的新切片,然后将新元素复制到该切片中。
func extend(v any) any {
rv := reflect.ValueOf(v)
extended := reflect.MakeSlice(rv.Type(), rv.Len(), rv.Cap()*2+1)
reflect.Copy(extended, rv)
return extended.Interface()
}