for range peste funcții (iteratori) în Go

Din Go 1.23, for range parcurge funcții iterator: funcții care trimit pe rând valorile prin yield.

Sintaxă
func(yield func(V) bool)       // iter.Seq[V]
func(yield func(K, V) bool)    // iter.Seq2[K, V]

for v := range seq { ... }

Ce face for range peste funcții (iteratori)

Un iterator este o funcție care primește o altă funcție, numită de obicei yield, și o apelează pentru fiecare valoare pe care vrea să o dea. Dacă yield returnează false (bucla a făcut break sau return), iteratorul trebuie să se oprească imediat. Pachetul iter denumește aceste forme: iter.Seq[V] pentru o valoare și iter.Seq2[K, V] pentru perechi.

Din Go 1.23, astfel de funcții se pot parcurge direct cu for v := range seq. Biblioteca standard oferă multe iteratoare: maps.Keys, maps.All, slices.Values, slices.Backward, strings.SplitSeq, iar slices.Collect și slices.Sorted le transformă înapoi în slice-uri.

Avantajul: valorile se produc doar când sunt cerute, deci poți parcurge șiruri foarte lungi sau infinite fără să le ții în memorie.

Exemple

Exemplul 1: Un iterator propriu

pare dă valorile pe rând; break face ca yield să returneze false.

package main

import (
    "fmt"
    "iter"
)

func pare(limita int) iter.Seq[int] {
    return func(yield func(int) bool) {
        for i := 0; i <= limita; i += 2 {
            if !yield(i) {
                return
            }
        }
    }
}

func main() {
    for n := range pare(10) {
        fmt.Print(n, " ")
    }
    fmt.Println()

    for n := range pare(1000000) {
        if n > 6 {
            break
        }
        fmt.Print(n, " ")
    }
    fmt.Println()
}

Rezultat

0 2 4 6 8 10 
0 2 4 6

Exemplul 2: Iterator cu perechi (iter.Seq2)

package main

import (
    "fmt"
    "iter"
)

type Elev struct {
    Nume string
    Nota int
}

func promovati(elevi []Elev) iter.Seq2[int, Elev] {
    return func(yield func(int, Elev) bool) {
        for i, e := range elevi {
            if e.Nota >= 5 && !yield(i, e) {
                return
            }
        }
    }
}

func main() {
    clasa := []Elev{{"Ana", 9}, {"Mihai", 4}, {"Ioana", 7}}
    for i, e := range promovati(clasa) {
        fmt.Printf("%d: %s (%d)\n", i, e.Nume, e.Nota)
    }
}

Rezultat

0: Ana (9)
2: Ioana (7)

Exemplul 3: Iteratori din biblioteca standard

package main

import (
    "fmt"
    "maps"
    "slices"
    "strings"
)

func main() {
    stoc := map[string]int{"mere": 12, "pere": 0, "prune": 5}
    for _, fruct := range slices.Sorted(maps.Keys(stoc)) {
        fmt.Println(fruct, stoc[fruct])
    }

    for i, v := range slices.Backward([]string{"a", "b", "c"}) {
        fmt.Print(i, v, " ")
    }
    fmt.Println()

    for parte := range strings.SplitSeq("Iași,Cluj,Arad", ",") {
        fmt.Println("oraș:", parte)
    }
}

Rezultat

mere 12
pere 0
prune 5
2c 1b 0a 
oraș: Iași
oraș: Cluj
oraș: Arad

Greșeli frecvente

  • Ignorarea rezultatului lui yield: dacă bucla face break și iteratorul mai apelează yield, programul se oprește cu panic: runtime error: range function continued iteration after function for loop body returned false.
  • Folosirea iteratorilor în Go mai vechi de 1.23: nu compilează.

Comenzi înrudite

  • range: Parcurge într-un for elementele unui slice, array, map, text, canal, număr sau iterator.
  • for: Singura buclă din Go: forma clasică, forma „while”, bucla infinită și parcurgerea cu range.
  • closures (funcții anonime): Funcții anonime care „țin minte” variabilele din jurul lor și le pot citi și modifica după ce funcția exterioară s-a terminat.
  • generics (parametri de tip): Funcții și tipuri care lucrează cu mai multe tipuri, scrise o singură dată cu parametri de tip [T constrângere].