Skip to content

Commit d17a28a

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 three-clause for loops where possible. Fixes golang#66631
1 parent 34e6762 commit d17a28a

File tree

7 files changed

+115
-15
lines changed

7 files changed

+115
-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

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: 57 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"
@@ -255,6 +256,22 @@ type Type interface {
255256
// CanSeq2 reports whether a [Value] with this type can be iterated over using [Value.Seq2].
256257
CanSeq2() bool
257258

259+
// Fields yields each field of a struct type.
260+
// It panics if the type's Kind is not Struct.
261+
Fields() iter.Seq[StructField]
262+
263+
// Methods yields each method in the type's method set.
264+
// See [Type.Method] for information on the yielded methods.
265+
Methods() iter.Seq[Method]
266+
267+
// Ins yields each input parameter of a function type, in order.
268+
// It panics if the type's Kind is not Func.
269+
Ins() iter.Seq[Type]
270+
271+
// Outs yields each output parameter of a function type, in order.
272+
// It panics if the type's Kind is not Func.
273+
Outs() iter.Seq[Type]
274+
258275
common() *abi.Type
259276
uncommon() *uncommonType
260277
}
@@ -934,6 +951,46 @@ func canRangeFunc2(t *abi.Type) bool {
934951
return yield.InCount == 2 && yield.OutCount == 1 && yield.Out(0).Kind() == abi.Bool
935952
}
936953

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

src/reflect/value.go

Lines changed: 44 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,47 @@ func (v Value) UnsafePointer() unsafe.Pointer {
26202621
panic(&ValueError{"reflect.Value.UnsafePointer", v.kind()})
26212622
}
26222623

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

32233265
case Struct:
3224-
for i := 0; i < v.NumField(); i++ {
3225-
if !v.Field(i).Comparable() {
3266+
for _, value := range v.Fields() {
3267+
if !value.Comparable() {
32263268
return false
32273269
}
32283270
}

0 commit comments

Comments
 (0)