-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Open
Labels
LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposal
Milestone
Description
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:
Lines 1535 to 1553 in b9545da
| // 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) |
Lines 1559 to 1562 in b9545da
| // 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
Lines 1535 to 1576 in b9545da
| // 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
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposal