Skip to content

Commit 38b1a42

Browse files
committed
reflect: add iterator equivalents for NumField, NumIn, NumOut and NumMethod
The new methods are Type.Fields, Type.Methods, Type.Ins, Type.Outs, Value.Fields and Value.Methods. These methods have been introduced into the reflect package (as well as tests) replacing any three-clause for loops where possible. Fixes #66631
1 parent 34e6762 commit 38b1a42

File tree

8 files changed

+124
-15
lines changed

8 files changed

+124
-15
lines changed

api/next/66631.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pkg reflect, type Type interface, Fields() iter.Seq[StructField] #66631
2+
pkg reflect, type Type interface, Methods() iter.Seq[Method] #66631
3+
pkg reflect, type Type interface, Ins() iter.Seq[Type] #66631
4+
pkg reflect, type Type interface, Outs() iter.Seq[Type] #66631
5+
pkg reflect, method (Value) Fields() iter.Seq2[StructField, Value] #66631
6+
pkg reflect, method (Value) Methods() iter.Seq2[Method, Value] #66631
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[reflect.Type] includes new methods that return iterators for a type's fields, methods, inputs and outputs.
2+
[reflect.Value.Fields] and [reflect.Value.Methods] have also been introduced, these return iterators which
3+
yield both a [reflect.Value] as well as the corresponding [reflect.StructField] or [reflect.Method].

src/reflect/abi_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ func TestReflectCallABI(t *testing.T) {
175175
t.Fatalf("test case has different number of inputs and outputs: %d in, %d out", typ.NumIn(), typ.NumOut())
176176
}
177177
var args []reflect.Value
178-
for i := 0; i < typ.NumIn(); i++ {
179-
args = append(args, genValue(t, typ.In(i), r))
178+
for arg := range typ.Ins() {
179+
args = append(args, genValue(t, arg, r))
180180
}
181181
results := fn.Call(args)
182182
for i := range results {

src/reflect/all_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8505,8 +8505,7 @@ func TestInitFuncTypes(t *testing.T) {
85058505
go func() {
85068506
defer wg.Done()
85078507
ipT := TypeOf(net.IP{})
8508-
for i := 0; i < ipT.NumMethod(); i++ {
8509-
_ = ipT.Method(i)
8508+
for range ipT.Methods() {
85108509
}
85118510
}()
85128511
}

src/reflect/benchmark_test.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,8 @@ func BenchmarkIsZero(b *testing.B) {
146146
s.ArrayInt_1024_NoZero[512] = 1
147147
source := ValueOf(s)
148148

149-
for i := 0; i < source.NumField(); i++ {
150-
name := source.Type().Field(i).Name
151-
value := source.Field(i)
152-
b.Run(name, func(b *testing.B) {
149+
for field, value := range source.Fields() {
150+
b.Run(field.Name, func(b *testing.B) {
153151
for i := 0; i < b.N; i++ {
154152
sink = value.IsZero()
155153
}
@@ -175,9 +173,8 @@ func BenchmarkSetZero(b *testing.B) {
175173
Struct Value
176174
})).Elem()
177175

178-
for i := 0; i < source.NumField(); i++ {
179-
name := source.Type().Field(i).Name
180-
value := source.Field(i)
176+
for field, value := range source.Fields() {
177+
name := field.Name
181178
zero := Zero(value.Type())
182179
b.Run(name+"/Direct", func(b *testing.B) {
183180
for i := 0; i < b.N; i++ {

src/reflect/example_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ func ExampleStructTag_Lookup() {
9696

9797
s := S{}
9898
st := reflect.TypeOf(s)
99-
for i := 0; i < st.NumField(); i++ {
100-
field := st.Field(i)
99+
for field := range st.Fields() {
101100
if alias, ok := field.Tag.Lookup("alias"); ok {
102101
if alias == "" {
103102
fmt.Println("(blank)")

src/reflect/type.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package reflect
1818
import (
1919
"internal/abi"
2020
"internal/goarch"
21+
"iter"
2122
"runtime"
2223
"strconv"
2324
"sync"
@@ -64,6 +65,10 @@ type Type interface {
6465
// This may make the executable binary larger but will not affect execution time.
6566
Method(int) Method
6667

68+
// Methods returns an iterator that yields each method in the type's method set. The order is
69+
// equivalent to calling Method successively for each index i in the range [0, NumMethod()).
70+
Methods() iter.Seq[Method]
71+
6772
// MethodByName returns the method with that name in the type's
6873
// method set and a boolean indicating if the method was found.
6974
//
@@ -172,6 +177,11 @@ type Type interface {
172177
// It panics if i is not in the range [0, NumField()).
173178
Field(i int) StructField
174179

180+
// Fields returns an iterator that yields each struct field for struct type t. The order is
181+
// equivalent to calling Field successively for each index i in the range [0, NumField()).
182+
// It panics if the type's Kind is not Struct.
183+
Fields() iter.Seq[StructField]
184+
175185
// FieldByIndex returns the nested field corresponding
176186
// to the index sequence. It is equivalent to calling Field
177187
// successively for each index i.
@@ -208,6 +218,11 @@ type Type interface {
208218
// It panics if i is not in the range [0, NumIn()).
209219
In(i int) Type
210220

221+
// Ins returns an iterator that yields each input parameter of function type t. The order
222+
// is equivalent to calling In successively for each index i in the range [0, NumIn()).
223+
// It panics if the type's Kind is not Func.
224+
Ins() iter.Seq[Type]
225+
211226
// Key returns a map type's key type.
212227
// It panics if the type's Kind is not Map.
213228
Key() Type
@@ -233,6 +248,11 @@ type Type interface {
233248
// It panics if i is not in the range [0, NumOut()).
234249
Out(i int) Type
235250

251+
// Outs returns an iterator that yields each output parameter of function type t. The order
252+
// is equivalent to calling Out successively for each index i in the range [0, NumOut()).
253+
// It panics if the type's Kind is not Func.
254+
Outs() iter.Seq[Type]
255+
236256
// OverflowComplex reports whether the complex128 x cannot be represented by type t.
237257
// It panics if t's Kind is not Complex64 or Complex128.
238258
OverflowComplex(x complex128) bool
@@ -934,6 +954,46 @@ func canRangeFunc2(t *abi.Type) bool {
934954
return yield.InCount == 2 && yield.OutCount == 1 && yield.Out(0).Kind() == abi.Bool
935955
}
936956

957+
func (t *rtype) Fields() iter.Seq[StructField] {
958+
return func(yield func(StructField) bool) {
959+
for i := range t.NumField() {
960+
if !yield(t.Field(i)) {
961+
return
962+
}
963+
}
964+
}
965+
}
966+
967+
func (t *rtype) Methods() iter.Seq[Method] {
968+
return func(yield func(Method) bool) {
969+
for i := range t.NumMethod() {
970+
if !yield(t.Method(i)) {
971+
return
972+
}
973+
}
974+
}
975+
}
976+
977+
func (t *rtype) Ins() iter.Seq[Type] {
978+
return func(yield func(Type) bool) {
979+
for i := range t.NumIn() {
980+
if !yield(t.In(i)) {
981+
return
982+
}
983+
}
984+
}
985+
}
986+
987+
func (t *rtype) Outs() iter.Seq[Type] {
988+
return func(yield func(Type) bool) {
989+
for i := range t.NumOut() {
990+
if !yield(t.Out(i)) {
991+
return
992+
}
993+
}
994+
}
995+
}
996+
937997
// add returns p+x.
938998
//
939999
// The whySafe string is ignored, so that the function still inlines

src/reflect/value.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"internal/goarch"
1111
"internal/itoa"
1212
"internal/unsafeheader"
13+
"iter"
1314
"math"
1415
"runtime"
1516
"unsafe"
@@ -2620,6 +2621,50 @@ func (v Value) UnsafePointer() unsafe.Pointer {
26202621
panic(&ValueError{"reflect.Value.UnsafePointer", v.kind()})
26212622
}
26222623

2624+
// Fields returns an iterator that yields each field of v, along with a [StructField]
2625+
// description of it.
2626+
//
2627+
// The iteration order is equivalent to calling [Value.Field]
2628+
// successively for each index i in the range [0, NumField()).
2629+
//
2630+
// It panics if v's Kind is not Struct.
2631+
func (v Value) Fields() iter.Seq2[StructField, Value] {
2632+
return func(yield func(StructField, Value) bool) {
2633+
rtype := v.Type()
2634+
for i := range v.NumField() {
2635+
if !yield(rtype.Field(i), v.Field(i)) {
2636+
return
2637+
}
2638+
}
2639+
}
2640+
}
2641+
2642+
// Methods returns an iterator that yields a function value for each method of v,
2643+
// along with a [Method] description of it.
2644+
//
2645+
// The iteration order is equivalent to calling [Value.Method] successively for
2646+
// each index i in the range [0, NumMethod()).
2647+
//
2648+
// The arguments to a Call on the yielded function value should not include a receiver;
2649+
// the returned function will always use v as the receiver. Note that [Method.Type]
2650+
// in each yielded [Method] does include the receiver, and thus is not the same as
2651+
// [Value.Type].
2652+
//
2653+
// Methods panics if v is a nil interface value.
2654+
//
2655+
// Calling this method will force the linker to retain all exported methods in all packages.
2656+
// This may make the executable binary larger but will not affect execution time.
2657+
func (v Value) Methods() iter.Seq2[Method, Value] {
2658+
return func(yield func(Method, Value) bool) {
2659+
rtype := v.Type()
2660+
for i := range v.NumMethod() {
2661+
if !yield(rtype.Method(i), v.Method(i)) {
2662+
return
2663+
}
2664+
}
2665+
}
2666+
}
2667+
26232668
// StringHeader is the runtime representation of a string.
26242669
// It cannot be used safely or portably and its representation may
26252670
// change in a later release.
@@ -3221,8 +3266,8 @@ func (v Value) Comparable() bool {
32213266
return v.IsNil() || v.Elem().Comparable()
32223267

32233268
case Struct:
3224-
for i := 0; i < v.NumField(); i++ {
3225-
if !v.Field(i).Comparable() {
3269+
for _, value := range v.Fields() {
3270+
if !value.Comparable() {
32263271
return false
32273272
}
32283273
}

0 commit comments

Comments
 (0)