I want to extend the capacity of a slice using reflection, but the variable itself is an interface{} and I need to get the underlying type first. However, trying to access the underlying type results in having an unaddressable value that I can't extend the capacity of. I know I need to pass in a pointer to the slice, because otherwise the unaddressable value won't bubble my changes up, but if I pass a pointer to reflect.ValueOf, I get a Pointer->Interface->Slice situation, whereas I want Pointer->Slice, i.e. an addressable version of the slice.
I've got a minimal example thrown together to demonstrate the problem. The extend function receives a variable of type any that is actually a slice (could be a slice of any type, e.g. []any, []string, []MyStruct). In my particular situation I can't use generics, this example is just a boilt down version of the preconditions that led here.
In my example I have the things I have tried listed, also available online: 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)))
}
If the type is not known until runtime and you can't use
slices.Grow, you can perform the same operation using thereflectpackage.The method for extending the capacity of a slice using
makeandcopydirectly translates to functions available throughreflect. You allocate a new slice of the correct type, length and capacity, then you copy the new elements into that slice.