Skip to content

Commit e8fdfeb

Browse files
Splizardadonovan
authored andcommitted
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 three-clause for loops where possible. Fixes #66631 Change-Id: Iab346e52c0eadd7817afae96d9ef73a35db65fd2 GitHub-Last-Rev: 8768ef7 GitHub-Pull-Request: #75646 Reviewed-on: https://go-review.googlesource.com/c/go/+/707356 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Alan Donovan <adonovan@google.com> Auto-Submit: Alan Donovan <adonovan@google.com>
1 parent 12d437c commit e8fdfeb

File tree

8 files changed

+132
-15
lines changed

8 files changed

+132
-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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[reflect.Type] includes new methods that return iterators for a type's fields, methods, inputs and outputs.
2+
Similarly, [reflect.Value] includes two new methods that return iterators over a value's fields or methods,
3+
each element being a pair of the value ([reflect.Value]) and its type information ([reflect.StructField] or
4+
[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
@@ -8491,8 +8491,7 @@ func TestInitFuncTypes(t *testing.T) {
84918491
go func() {
84928492
defer wg.Done()
84938493
ipT := TypeOf(net.IP{})
8494-
for i := 0; i < ipT.NumMethod(); i++ {
8495-
_ = ipT.Method(i)
8494+
for range ipT.Methods() {
84968495
}
84978496
}()
84988497
}

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: 69 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 over each method in the type's method set. The sequence 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 over each struct field for struct type t. The sequence 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 over each input parameter of function type t. The sequence
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 over each output parameter of function type t. The sequence
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
@@ -937,6 +957,55 @@ func canRangeFunc2(t *abi.Type) bool {
937957
return yield.InCount == 2 && yield.OutCount == 1 && yield.Out(0).Kind() == abi.Bool
938958
}
939959

960+
func (t *rtype) Fields() iter.Seq[StructField] {
961+
if t.Kind() != Struct {
962+
panic("reflect: Fields of non-struct type " + t.String())
963+
}
964+
return func(yield func(StructField) bool) {
965+
for i := range t.NumField() {
966+
if !yield(t.Field(i)) {
967+
return
968+
}
969+
}
970+
}
971+
}
972+
973+
func (t *rtype) Methods() iter.Seq[Method] {
974+
return func(yield func(Method) bool) {
975+
for i := range t.NumMethod() {
976+
if !yield(t.Method(i)) {
977+
return
978+
}
979+
}
980+
}
981+
}
982+
983+
func (t *rtype) Ins() iter.Seq[Type] {
984+
if t.Kind() != Func {
985+
panic("reflect: Ins of non-func type " + t.String())
986+
}
987+
return func(yield func(Type) bool) {
988+
for i := range t.NumIn() {
989+
if !yield(t.In(i)) {
990+
return
991+
}
992+
}
993+
}
994+
}
995+
996+
func (t *rtype) Outs() iter.Seq[Type] {
997+
if t.Kind() != Func {
998+
panic("reflect: Outs of non-func type " + t.String())
999+
}
1000+
return func(yield func(Type) bool) {
1001+
for i := range t.NumOut() {
1002+
if !yield(t.Out(i)) {
1003+
return
1004+
}
1005+
}
1006+
}
1007+
}
1008+
9401009
// add returns p+x.
9411010
//
9421011
// The whySafe string is ignored, so that the function still inlines

src/reflect/value.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"internal/goarch"
1111
"internal/strconv"
1212
"internal/unsafeheader"
13+
"iter"
1314
"math"
1415
"runtime"
1516
"unsafe"
@@ -2631,6 +2632,48 @@ func (v Value) UnsafePointer() unsafe.Pointer {
26312632
panic(&ValueError{"reflect.Value.UnsafePointer", v.kind()})
26322633
}
26332634

2635+
// Fields returns an iterator over each [StructField] of v along with its [Value].
2636+
//
2637+
// The sequence is equivalent to calling [Value.Field] successively
2638+
// for each index i in the range [0, NumField()).
2639+
//
2640+
// It panics if v's Kind is not Struct.
2641+
func (v Value) Fields() iter.Seq2[StructField, Value] {
2642+
t := v.Type()
2643+
if t.Kind() != Struct {
2644+
panic("reflect: Fields of non-struct type " + t.String())
2645+
}
2646+
return func(yield func(StructField, Value) bool) {
2647+
for i := range v.NumField() {
2648+
if !yield(t.Field(i), v.Field(i)) {
2649+
return
2650+
}
2651+
}
2652+
}
2653+
}
2654+
2655+
// Methods returns an iterator over each [Method] of v along with the corresponding
2656+
// method [Value]; this is a function with v bound as the receiver. As such, the
2657+
// receiver shouldn't be included in the arguments to [Value.Call].
2658+
//
2659+
// The sequence is equivalent to calling [Value.Method] successively
2660+
// for each index i in the range [0, NumMethod()).
2661+
//
2662+
// Methods panics if v is a nil interface value.
2663+
//
2664+
// Calling this method will force the linker to retain all exported methods in all packages.
2665+
// This may make the executable binary larger but will not affect execution time.
2666+
func (v Value) Methods() iter.Seq2[Method, Value] {
2667+
return func(yield func(Method, Value) bool) {
2668+
rtype := v.Type()
2669+
for i := range v.NumMethod() {
2670+
if !yield(rtype.Method(i), v.Method(i)) {
2671+
return
2672+
}
2673+
}
2674+
}
2675+
}
2676+
26342677
// StringHeader is the runtime representation of a string.
26352678
// It cannot be used safely or portably and its representation may
26362679
// change in a later release.
@@ -3232,8 +3275,8 @@ func (v Value) Comparable() bool {
32323275
return v.IsNil() || v.Elem().Comparable()
32333276

32343277
case Struct:
3235-
for i := 0; i < v.NumField(); i++ {
3236-
if !v.Field(i).Comparable() {
3278+
for _, value := range v.Fields() {
3279+
if !value.Comparable() {
32373280
return false
32383281
}
32393282
}

0 commit comments

Comments
 (0)