Skip to content

Commit

Permalink
reflect: add iterative related methods
Browse files Browse the repository at this point in the history
Fixes #66056

Change-Id: I1e24636e43e68cd57576c39b014e0826fb6c322c
GitHub-Last-Rev: 319ad8e
GitHub-Pull-Request: #66824
Reviewed-on: https://go-review.googlesource.com/c/go/+/578815
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
  • Loading branch information
qiulaidongfeng authored and gopherbot committed May 9, 2024
1 parent 49eedfb commit 524a774
Show file tree
Hide file tree
Showing 6 changed files with 540 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/next/66056.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pkg reflect, method (Value) Seq() iter.Seq[Value] #66056
pkg reflect, method (Value) Seq2() iter.Seq2[Value, Value] #66056
pkg reflect, type Type interface, CanSeq() bool #66056
pkg reflect, type Type interface, CanSeq2() bool #66056
4 changes: 4 additions & 0 deletions doc/next/6-stdlib/99-minor/reflect/66056.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The new methods [Value.Seq] and [Value.Seq2] return sequences that iterate over the value
as though it were used in a for/range loop.
The new methods [Type.CanSeq] and [Type.CanSeq2] report whether calling
[Value.Seq] and [Value.Seq2], respectively, will succeed without panicking.
140 changes: 140 additions & 0 deletions src/reflect/iter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package reflect

import "iter"

// Seq returns an iter.Seq[reflect.Value] that loops over the elements of v.
// If v's kind is Func, it must be a function that has no results and
// that takes a single argument of type func(T) bool for some type T.
// If v's kind is Pointer, the pointer element type must have kind Array.
// Otherwise v's kind must be Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr,
// Array, Chan, Map, Slice, or String.
func (v Value) Seq() iter.Seq[Value] {
if canRangeFunc(v.typ()) {
return func(yield func(Value) bool) {
rf := MakeFunc(v.Type().In(0), func(in []Value) []Value {
return []Value{ValueOf(yield(in[0]))}
})
v.Call([]Value{rf})
}
}
switch v.Kind() {
case Int, Int8, Int16, Int32, Int64:
return func(yield func(Value) bool) {
for i := range v.Int() {
if !yield(ValueOf(i)) {
return
}
}
}
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return func(yield func(Value) bool) {
for i := range v.Uint() {
if !yield(ValueOf(i)) {
return
}
}
}
case Pointer:
if v.Elem().kind() != Array {
break
}
return func(yield func(Value) bool) {
v = v.Elem()
for i := range v.Len() {
if !yield(ValueOf(i)) {
return
}
}
}
case Array, Slice:
return func(yield func(Value) bool) {
for i := range v.Len() {
if !yield(ValueOf(i)) {
return
}
}
}
case String:
return func(yield func(Value) bool) {
for i := range v.String() {
if !yield(ValueOf(i)) {
return
}
}
}
case Map:
return func(yield func(Value) bool) {
i := v.MapRange()
for i.Next() {
if !yield(i.Key()) {
return
}
}
}
case Chan:
return func(yield func(Value) bool) {
for value, ok := v.Recv(); ok; value, ok = v.Recv() {
if !yield(value) {
return
}
}
}
}
panic("reflect: " + v.Type().String() + " cannot produce iter.Seq[Value]")
}

// Seq2 is like Seq but for two values.
func (v Value) Seq2() iter.Seq2[Value, Value] {
if canRangeFunc2(v.typ()) {
return func(yield func(Value, Value) bool) {
rf := MakeFunc(v.Type().In(0), func(in []Value) []Value {
return []Value{ValueOf(yield(in[0], in[1]))}
})
v.Call([]Value{rf})
}
}
switch v.Kind() {
case Pointer:
if v.Elem().kind() != Array {
break
}
return func(yield func(Value, Value) bool) {
v = v.Elem()
for i := range v.Len() {
if !yield(ValueOf(i), v.Index(i)) {
return
}
}
}
case Array, Slice:
return func(yield func(Value, Value) bool) {
for i := range v.Len() {
if !yield(ValueOf(i), v.Index(i)) {
return
}
}
}
case String:
return func(yield func(Value, Value) bool) {
for i, v := range v.String() {
if !yield(ValueOf(i), ValueOf(v)) {
return
}
}
}
case Map:
return func(yield func(Value, Value) bool) {
i := v.MapRange()
for i.Next() {
if !yield(i.Key(), i.Value()) {
return
}
}
}
}
panic("reflect: " + v.Type().String() + " cannot produce iter.Seq2[Value, Value]")
}

0 comments on commit 524a774

Please sign in to comment.