Skip to content

Commit 10ff4ad

Browse files
committed
reflect: add TypeAssert[T]
1 parent ff27d27 commit 10ff4ad

File tree

4 files changed

+115
-0
lines changed

4 files changed

+115
-0
lines changed

api/next/62121.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkg reflect, func TypeAssert[$0 interface{}](Value) ($0, bool) #62121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The new [TypeAssert] function permits converting a [Value] directly to a Go type.
2+
This is like using a type assertion on the result of [Value.Interface].

src/reflect/all_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"math/rand"
2020
"net"
2121
"os"
22+
"reflect"
2223
. "reflect"
2324
"reflect/internal/example1"
2425
"reflect/internal/example2"
@@ -8681,3 +8682,83 @@ func TestMapOfKeyPanic(t *testing.T) {
86818682
var slice []int
86828683
m.MapIndex(ValueOf(slice))
86838684
}
8685+
8686+
func testTypeAssert[T comparable](t *testing.T, val T) {
8687+
t.Helper()
8688+
v, ok := TypeAssert[T](ValueOf(val))
8689+
if v != val || !ok {
8690+
t.Errorf("TypeAssert[%T](%v) = (%v, %v); want = (%v, true)", *new(T), val, v, ok, val)
8691+
}
8692+
}
8693+
8694+
func testTypeAssertDifferentType[T, T2 comparable](t *testing.T, val T2) {
8695+
t.Helper()
8696+
if v, ok := TypeAssert[T](ValueOf(val)); ok {
8697+
t.Errorf("TypeAssert[%T](%v) = (%v, %v); want = (%v, false)", *new(T), val, v, ok, *new(T))
8698+
}
8699+
}
8700+
8701+
func newPtr[T any](t T) *T {
8702+
return &t
8703+
}
8704+
8705+
func TestTypeAssert(t *testing.T) {
8706+
testTypeAssert(t, int(1111))
8707+
testTypeAssert(t, int(111111111))
8708+
testTypeAssert(t, int(-111111111))
8709+
testTypeAssert(t, int32(111111111))
8710+
testTypeAssert(t, int32(-111111111))
8711+
testTypeAssert(t, uint32(111111111))
8712+
testTypeAssert(t, [2]int{111111111, 22222222})
8713+
testTypeAssert(t, [2]int{-111111111, -22222222})
8714+
testTypeAssert(t, newPtr(1111))
8715+
testTypeAssert(t, newPtr(111111111))
8716+
testTypeAssert(t, newPtr(-111111111))
8717+
testTypeAssert(t, newPtr([2]int{-111111111, -22222222}))
8718+
8719+
testTypeAssert(t, newPtr(time.Now()))
8720+
8721+
testTypeAssertDifferentType[uint](t, int(111111111))
8722+
testTypeAssertDifferentType[uint](t, int(-111111111))
8723+
}
8724+
8725+
type testTypeWithMethod struct {
8726+
val string
8727+
}
8728+
8729+
func (v testTypeWithMethod) String() string { return v.val }
8730+
8731+
func TestTypeAssertMethod(t *testing.T) {
8732+
method := ValueOf(&testTypeWithMethod{val: "test value"}).MethodByName("String")
8733+
f, ok := TypeAssert[func() string](method)
8734+
if !ok {
8735+
t.Fatalf(`TypeAssert[func() string](method) = (,false); want = (,true)`)
8736+
}
8737+
8738+
out := f()
8739+
if out != "test value" {
8740+
t.Fatalf(`TypeAssert[func() string](method)() = %q; want "test value"`, out)
8741+
}
8742+
}
8743+
8744+
func TestTypeAssertZeroValPanic(t *testing.T) {
8745+
defer func() { recover() }()
8746+
TypeAssert[int](Value{})
8747+
t.Fatalf("TypeAssert did not panic")
8748+
}
8749+
8750+
func TestTypeAssertReadOnlyPanic(t *testing.T) {
8751+
defer func() { recover() }()
8752+
TypeAssert[int](reflect.ValueOf(&testTypeWithMethod{}).FieldByName("val"))
8753+
t.Fatalf("TypeAssert did not panic")
8754+
}
8755+
8756+
func TestTypeAssertAllocs(t *testing.T) {
8757+
val := ValueOf(new(time.Time)).Elem()
8758+
allocs := testing.AllocsPerRun(100, func() {
8759+
TypeAssert[time.Time](val)
8760+
})
8761+
if allocs != 0 {
8762+
t.Errorf("unexpected amount of allocations = %v; want = 0", allocs)
8763+
}
8764+
}

src/reflect/value.go

+31
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,37 @@ func valueInterface(v Value, safe bool) any {
15101510
return packEface(v)
15111511
}
15121512

1513+
// TypeAssert is semantically equivalent to:
1514+
//
1515+
// v2, ok := v.Interface().(T)
1516+
func TypeAssert[T any](v Value) (T, bool) {
1517+
if v.flag == 0 {
1518+
panic(&ValueError{"reflect.TypeAssert", Invalid})
1519+
}
1520+
1521+
if v.flag&flagRO != 0 {
1522+
// Do not allow access to unexported values via Interface,
1523+
// because they might be pointers that should not be
1524+
// writable or methods or function that should not be callable.
1525+
panic("reflect.TypeAssert: cannot return value obtained from unexported field or method")
1526+
}
1527+
1528+
if v.flag&flagMethod != 0 {
1529+
v = makeMethodValue("TypeAssert", v)
1530+
}
1531+
1532+
if abi.TypeFor[T]() != v.typ_ {
1533+
var zero T
1534+
return zero, false
1535+
}
1536+
1537+
if v.typ_.IsDirectIface() {
1538+
return *(*T)(unsafe.Pointer(&v.ptr)), true
1539+
}
1540+
1541+
return *(*T)(v.ptr), true
1542+
}
1543+
15131544
// InterfaceData returns a pair of unspecified uintptr values.
15141545
// It panics if v's Kind is not Interface.
15151546
//

0 commit comments

Comments
 (0)