使用反射扩展接口类型中包裹的切片的容量

问题描述 投票:0回答:1

我想使用反射扩展切片的容量,但变量本身是一个

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)))
}

go reflection
1个回答
0
投票

如果直到运行时才知道类型,并且无法使用

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()
}
© www.soinside.com 2019 - 2024. All rights reserved.