@@ -254,12 +254,17 @@ var (
254
254
//
255
255
// Example usage:
256
256
//
257
- // iter := iterable.Iterator()
257
+ // var seq Iterator = ...
258
+ // iter := seq.Iterate()
258
259
// defer iter.Done()
259
- // var x Value
260
- // for iter.Next(&x ) {
260
+ // var elem Value
261
+ // for iter.Next(elem ) {
261
262
// ...
262
263
// }
264
+ //
265
+ // Or, using go1.23 iterators:
266
+ //
267
+ // for elem := range Elements(seq) { ... }
263
268
type Iterator interface {
264
269
// If the iterator is exhausted, Next returns false.
265
270
// Otherwise it sets *p to the current element of the sequence,
@@ -283,6 +288,8 @@ type Mapping interface {
283
288
}
284
289
285
290
// An IterableMapping is a mapping that supports key enumeration.
291
+ //
292
+ // See [Entries] for example use.
286
293
type IterableMapping interface {
287
294
Mapping
288
295
Iterate () Iterator // see Iterable interface
@@ -847,6 +854,7 @@ func (d *Dict) Type() string { return "dict"
847
854
func (d * Dict ) Freeze () { d .ht .freeze () }
848
855
func (d * Dict ) Truth () Bool { return d .Len () > 0 }
849
856
func (d * Dict ) Hash () (uint32 , error ) { return 0 , fmt .Errorf ("unhashable type: dict" ) }
857
+ func (d * Dict ) Entries (yield func (k , v Value ) bool ) { d .ht .entries (yield ) }
850
858
851
859
func (x * Dict ) Union (y * Dict ) * Dict {
852
860
z := new (Dict )
@@ -954,6 +962,23 @@ func (l *List) Iterate() Iterator {
954
962
return & listIterator {l : l }
955
963
}
956
964
965
+ // Elements is a go1.23 iterator over the elements of the list.
966
+ //
967
+ // Example:
968
+ //
969
+ // for elem := range list.Elements { ... }
970
+ func (l * List ) Elements (yield func (Value ) bool ) {
971
+ if ! l .frozen {
972
+ l .itercount ++
973
+ defer func () { l .itercount -- }()
974
+ }
975
+ for _ , x := range l .elems {
976
+ if ! yield (x ) {
977
+ break
978
+ }
979
+ }
980
+ }
981
+
957
982
func (x * List ) CompareSameType (op syntax.Token , y_ Value , depth int ) (bool , error ) {
958
983
y := y_ .(* List )
959
984
// It's tempting to check x == y as an optimization here,
@@ -1053,6 +1078,20 @@ func (t Tuple) Slice(start, end, step int) Value {
1053
1078
}
1054
1079
1055
1080
func (t Tuple ) Iterate () Iterator { return & tupleIterator {elems : t } }
1081
+
1082
+ // Elements is a go1.23 iterator over the elements of the tuple.
1083
+ //
1084
+ // (A Tuple is a slice, so it is of course directly iterable. This
1085
+ // method exists to provide a fast path for the [Elements] standalone
1086
+ // function.)
1087
+ func (t Tuple ) Elements (yield func (Value ) bool ) {
1088
+ for _ , x := range t {
1089
+ if ! yield (x ) {
1090
+ break
1091
+ }
1092
+ }
1093
+ }
1094
+
1056
1095
func (t Tuple ) Freeze () {
1057
1096
for _ , elem := range t {
1058
1097
elem .Freeze ()
@@ -1124,6 +1163,9 @@ func (s *Set) Truth() Bool { return s.Len() > 0 }
1124
1163
1125
1164
func (s * Set ) Attr (name string ) (Value , error ) { return builtinAttr (s , name , setMethods ) }
1126
1165
func (s * Set ) AttrNames () []string { return builtinAttrNames (setMethods ) }
1166
+ func (s * Set ) Elements (yield func (k Value ) bool ) {
1167
+ s .ht .entries (func (k , _ Value ) bool { return yield (k ) })
1168
+ }
1127
1169
1128
1170
func (x * Set ) CompareSameType (op syntax.Token , y_ Value , depth int ) (bool , error ) {
1129
1171
y := y_ .(* Set )
@@ -1561,6 +1603,74 @@ func Iterate(x Value) Iterator {
1561
1603
return nil
1562
1604
}
1563
1605
1606
+ // Elements returns an iterator for the elements of the iterable value.
1607
+ //
1608
+ // Example of go1.23 iteration:
1609
+ //
1610
+ // for elem := range Elements(iterable) { ... }
1611
+ //
1612
+ // Push iterators are provided as a convience for Go client code. The
1613
+ // core iteration behavior of Starlark for-loops is defined by the
1614
+ // [Iterable] interface.
1615
+ //
1616
+ // TODO(adonovan): change return type to go1.23 iter.Seq[Value].
1617
+ func Elements (iterable Iterable ) func (yield func (Value ) bool ) {
1618
+ // Use specialized push iterator if available (*List, Tuple, *Set).
1619
+ type hasElements interface {
1620
+ Elements (yield func (k Value ) bool )
1621
+ }
1622
+ if iterable , ok := iterable .(hasElements ); ok {
1623
+ return iterable .Elements
1624
+ }
1625
+
1626
+ iter := iterable .Iterate ()
1627
+ return func (yield func (Value ) bool ) {
1628
+ defer iter .Done ()
1629
+ var x Value
1630
+ for iter .Next (& x ) && yield (x ) {
1631
+ }
1632
+ }
1633
+ }
1634
+
1635
+ // Entries returns an iterator over the entries (key/value pairs) of
1636
+ // the iterable mapping.
1637
+ //
1638
+ // Example of go1.23 iteration:
1639
+ //
1640
+ // for k, v := range Entries(mapping) { ... }
1641
+ //
1642
+ // Push iterators are provided as a convience for Go client code. The
1643
+ // core iteration behavior of Starlark for-loops is defined by the
1644
+ // [Iterable] interface.
1645
+ //
1646
+ // TODO(adonovan): change return type to go1.23 iter.Seq2[Value, Value].
1647
+ func Entries (mapping IterableMapping ) func (yield func (k , v Value ) bool ) {
1648
+ // If available (e.g. *Dict), use specialized push iterator,
1649
+ // as it gets k and v in one shot.
1650
+ type hasEntries interface {
1651
+ Entries (yield func (k , v Value ) bool )
1652
+ }
1653
+ if mapping , ok := mapping .(hasEntries ); ok {
1654
+ return mapping .Entries
1655
+ }
1656
+
1657
+ iter := mapping .Iterate ()
1658
+ return func (yield func (k , v Value ) bool ) {
1659
+ defer iter .Done ()
1660
+ var k Value
1661
+ for iter .Next (& k ) {
1662
+ v , found , err := mapping .Get (k )
1663
+ if err != nil || ! found {
1664
+ panic (fmt .Sprintf ("Iterate and Get are inconsistent (mapping=%v, key=%v)" ,
1665
+ mapping .Type (), k .Type ()))
1666
+ }
1667
+ if ! yield (k , v ) {
1668
+ break
1669
+ }
1670
+ }
1671
+ }
1672
+ }
1673
+
1564
1674
// Bytes is the type of a Starlark binary string.
1565
1675
//
1566
1676
// A Bytes encapsulates an immutable sequence of bytes.
0 commit comments