EDIT: see #78007 (comment) for latest proposal
Proposal Details
In the reflect package we have two ways to convert a Value into a Go type, i.e. v.Interface.(T) and reflect.TypeAssert[T](v).
Both of these work on interface-basis, and are not easy to reason about (especially the TypeAssert, since the interface is "hidden" through generics), see the comments in the implementation, for all the corner-cases:
|
// If v is an interface, return the element inside the interface. |
|
// |
|
// T is a concrete type and v is an interface. For example: |
|
// |
|
// var v any = int(1) |
|
// val := ValueOf(&v).Elem() |
|
// TypeAssert[int](val) == val.Interface().(int) |
|
// |
|
// T is a interface and v is a non-nil interface value. For example: |
|
// |
|
// var v any = &someError{} |
|
// val := ValueOf(&v).Elem() |
|
// TypeAssert[error](val) == val.Interface().(error) |
|
// |
|
// T is a interface and v is a nil interface value. For example: |
|
// |
|
// var v error = nil |
|
// val := ValueOf(&v).Elem() |
|
// TypeAssert[error](val) == val.Interface().(error) |
|
// If T is an interface and v is a concrete type. For example: |
|
// |
|
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any) |
|
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error) |
What we are missing is a way to convert a Value directly into a Go type without all these interface unpacking/packing logic.
I propose we add:
// As attempts to unpack v into type T.
// It returns the value and true if v.Type() == reflect.TypeFor[T](),
// otherwise the zero value of T and false.
//
// Panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) As[T any]() (val T, ok bool)
Implementation-wise, it would be identical to TypeAssert, but without the special cases
|
// If v is an interface, return the element inside the interface. |
|
// |
|
// T is a concrete type and v is an interface. For example: |
|
// |
|
// var v any = int(1) |
|
// val := ValueOf(&v).Elem() |
|
// TypeAssert[int](val) == val.Interface().(int) |
|
// |
|
// T is a interface and v is a non-nil interface value. For example: |
|
// |
|
// var v any = &someError{} |
|
// val := ValueOf(&v).Elem() |
|
// TypeAssert[error](val) == val.Interface().(error) |
|
// |
|
// T is a interface and v is a nil interface value. For example: |
|
// |
|
// var v error = nil |
|
// val := ValueOf(&v).Elem() |
|
// TypeAssert[error](val) == val.Interface().(error) |
|
if v.kind() == Interface { |
|
v, ok := packIfaceValueIntoEmptyIface(v).(T) |
|
return v, ok |
|
} |
|
|
|
// If T is an interface and v is a concrete type. For example: |
|
// |
|
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any) |
|
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error) |
|
if typ.Kind() == abi.Interface { |
|
// To avoid allocating memory, in case the type assertion fails, |
|
// first do the type assertion with a nil Data pointer. |
|
iface := *(*any)(unsafe.Pointer(&abi.EmptyInterface{Type: v.typ(), Data: nil})) |
|
if out, ok := iface.(T); ok { |
|
// Now populate the Data field properly, we update the Data ptr |
|
// directly to avoid an additional type asertion. We can re-use the |
|
// itab we already got from the runtime (through the previous type assertion). |
|
(*abi.CommonInterface)(unsafe.Pointer(&out)).Data = packEfaceData(v) |
|
return out, true |
|
} |
|
var zero T |
|
return zero, false |
|
} |
Assumes #77273
EDIT: see #78007 (comment) for latest proposal
Proposal Details
In the reflect package we have two ways to convert a
Valueinto a Go type, i.e.v.Interface.(T)andreflect.TypeAssert[T](v).Both of these work on interface-basis, and are not easy to reason about (especially the
TypeAssert, since the interface is "hidden" through generics), see the comments in the implementation, for all the corner-cases:go/src/reflect/value.go
Lines 1535 to 1553 in b9545da
go/src/reflect/value.go
Lines 1559 to 1562 in b9545da
What we are missing is a way to convert a
Valuedirectly into a Go type without all these interface unpacking/packing logic.I propose we add:
Implementation-wise, it would be identical to
TypeAssert, but without the special casesgo/src/reflect/value.go
Lines 1535 to 1576 in b9545da
Assumes #77273