Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/next/66631.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pkg reflect, type Type interface, Fields() iter.Seq[StructField] #66631
pkg reflect, type Type interface, Methods() iter.Seq[Method] #66631
pkg reflect, type Type interface, Ins() iter.Seq[Type] #66631
pkg reflect, type Type interface, Outs() iter.Seq[Type] #66631
pkg reflect, method (Value) Fields() iter.Seq2[StructField, Value] #66631
pkg reflect, method (Value) Methods() iter.Seq2[Method, Value] #66631
3 changes: 3 additions & 0 deletions doc/next/6-stdlib/99-minor/reflect/66631.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[reflect.Type] includes new methods that return iterators for a type's fields, methods, inputs and outputs.
[reflect.Value.Fields] and [reflect.Value.Methods] have also been introduced, these return iterators which
yield both a [reflect.Value] as well as the corresponding [reflect.StructField] or [reflect.Method].
4 changes: 2 additions & 2 deletions src/reflect/abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ func TestReflectCallABI(t *testing.T) {
t.Fatalf("test case has different number of inputs and outputs: %d in, %d out", typ.NumIn(), typ.NumOut())
}
var args []reflect.Value
for i := 0; i < typ.NumIn(); i++ {
args = append(args, genValue(t, typ.In(i), r))
for arg := range typ.Ins() {
args = append(args, genValue(t, arg, r))
}
results := fn.Call(args)
for i := range results {
Expand Down
3 changes: 1 addition & 2 deletions src/reflect/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8505,8 +8505,7 @@ func TestInitFuncTypes(t *testing.T) {
go func() {
defer wg.Done()
ipT := TypeOf(net.IP{})
for i := 0; i < ipT.NumMethod(); i++ {
_ = ipT.Method(i)
for range ipT.Methods() {
}
}()
}
Expand Down
11 changes: 4 additions & 7 deletions src/reflect/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,8 @@ func BenchmarkIsZero(b *testing.B) {
s.ArrayInt_1024_NoZero[512] = 1
source := ValueOf(s)

for i := 0; i < source.NumField(); i++ {
name := source.Type().Field(i).Name
value := source.Field(i)
b.Run(name, func(b *testing.B) {
for field, value := range source.Fields() {
b.Run(field.Name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
sink = value.IsZero()
}
Expand All @@ -175,9 +173,8 @@ func BenchmarkSetZero(b *testing.B) {
Struct Value
})).Elem()

for i := 0; i < source.NumField(); i++ {
name := source.Type().Field(i).Name
value := source.Field(i)
for field, value := range source.Fields() {
name := field.Name
zero := Zero(value.Type())
b.Run(name+"/Direct", func(b *testing.B) {
for i := 0; i < b.N; i++ {
Expand Down
3 changes: 1 addition & 2 deletions src/reflect/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ func ExampleStructTag_Lookup() {

s := S{}
st := reflect.TypeOf(s)
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
for field := range st.Fields() {
if alias, ok := field.Tag.Lookup("alias"); ok {
if alias == "" {
fmt.Println("(blank)")
Expand Down
69 changes: 69 additions & 0 deletions src/reflect/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package reflect
import (
"internal/abi"
"internal/goarch"
"iter"
"runtime"
"strconv"
"sync"
Expand Down Expand Up @@ -64,6 +65,10 @@ type Type interface {
// This may make the executable binary larger but will not affect execution time.
Method(int) Method

// Methods returns an iterator over each method in the type's method set. The sequence is
// equivalent to calling Method successively for each index i in the range [0, NumMethod()).
Methods() iter.Seq[Method]

// MethodByName returns the method with that name in the type's
// method set and a boolean indicating if the method was found.
//
Expand Down Expand Up @@ -172,6 +177,11 @@ type Type interface {
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField

// Fields returns an iterator over each struct field for struct type t. The sequence is
// equivalent to calling Field successively for each index i in the range [0, NumField()).
// It panics if the type's Kind is not Struct.
Fields() iter.Seq[StructField]

// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
Expand Down Expand Up @@ -208,6 +218,11 @@ type Type interface {
// It panics if i is not in the range [0, NumIn()).
In(i int) Type

// Ins returns an iterator over each input parameter of function type t. The sequence
// is equivalent to calling In successively for each index i in the range [0, NumIn()).
// It panics if the type's Kind is not Func.
Ins() iter.Seq[Type]

// Key returns a map type's key type.
// It panics if the type's Kind is not Map.
Key() Type
Expand All @@ -233,6 +248,11 @@ type Type interface {
// It panics if i is not in the range [0, NumOut()).
Out(i int) Type

// Outs returns an iterator over each output parameter of function type t. The sequence
// is equivalent to calling Out successively for each index i in the range [0, NumOut()).
// It panics if the type's Kind is not Func.
Outs() iter.Seq[Type]

// OverflowComplex reports whether the complex128 x cannot be represented by type t.
// It panics if t's Kind is not Complex64 or Complex128.
OverflowComplex(x complex128) bool
Expand Down Expand Up @@ -934,6 +954,55 @@ func canRangeFunc2(t *abi.Type) bool {
return yield.InCount == 2 && yield.OutCount == 1 && yield.Out(0).Kind() == abi.Bool
}

func (t *rtype) Fields() iter.Seq[StructField] {
if t.Kind() != Struct {
panic("reflect: Fields of non-struct type " + t.String())
}
return func(yield func(StructField) bool) {
for i := range t.NumField() {
if !yield(t.Field(i)) {
return
}
}
}
}

func (t *rtype) Methods() iter.Seq[Method] {
return func(yield func(Method) bool) {
for i := range t.NumMethod() {
if !yield(t.Method(i)) {
return
}
}
}
}

func (t *rtype) Ins() iter.Seq[Type] {
if t.Kind() != Func {
panic("reflect: Ins of non-func type " + t.String())
}
return func(yield func(Type) bool) {
for i := range t.NumIn() {
if !yield(t.In(i)) {
return
}
}
}
}

func (t *rtype) Outs() iter.Seq[Type] {
if t.Kind() != Func {
panic("reflect: Outs of non-func type " + t.String())
}
return func(yield func(Type) bool) {
for i := range t.NumOut() {
if !yield(t.Out(i)) {
return
}
}
}
}

// add returns p+x.
//
// The whySafe string is ignored, so that the function still inlines
Expand Down
47 changes: 45 additions & 2 deletions src/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"internal/goarch"
"internal/itoa"
"internal/unsafeheader"
"iter"
"math"
"runtime"
"unsafe"
Expand Down Expand Up @@ -2620,6 +2621,48 @@ func (v Value) UnsafePointer() unsafe.Pointer {
panic(&ValueError{"reflect.Value.UnsafePointer", v.kind()})
}

// Fields returns an iterator over each [StructField] of v along with its [Value].
//
// The sequence is equivalent to calling [Value.Field] successively
// for each index i in the range [0, NumField()).
//
// It panics if v's Kind is not Struct.
func (v Value) Fields() iter.Seq2[StructField, Value] {
if t.Kind() != Struct {
panic("reflect: Fields of non-struct type " + t.String())
}
return func(yield func(StructField, Value) bool) {
rtype := v.Type()
for i := range v.NumField() {
if !yield(rtype.Field(i), v.Field(i)) {
return
}
}
}
}

// Methods returns an iterator over each [Method] of v along with the corresponding
// method [Value]; this is a function with v bound as the receiver. As such, the
// receiver shouldn't be included in the arguments to [Value.Call].
//
// The sequence is equivalent to calling [Value.Method] successively
// for each index i in the range [0, NumMethod()).
//
// Methods panics if v is a nil interface value.
//
// Calling this method will force the linker to retain all exported methods in all packages.
// This may make the executable binary larger but will not affect execution time.
func (v Value) Methods() iter.Seq2[Method, Value] {
return func(yield func(Method, Value) bool) {
rtype := v.Type()
for i := range v.NumMethod() {
if !yield(rtype.Method(i), v.Method(i)) {
return
}
}
}
}

// StringHeader is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
Expand Down Expand Up @@ -3221,8 +3264,8 @@ func (v Value) Comparable() bool {
return v.IsNil() || v.Elem().Comparable()

case Struct:
for i := 0; i < v.NumField(); i++ {
if !v.Field(i).Comparable() {
for _, value := range v.Fields() {
if !value.Comparable() {
return false
}
}
Expand Down