Iteratori Personalizați și Bucla for peste Funcții cu iter.Seq și iter.Pull în Go în Go

Ghid aprofundat pentru mecanismul Range-over-Func introdus în Go 1.22/1.23: crearea iteratorilor de tip Push (iter.Seq, iter.Seq2), conversia în iteratori de tip Pull cu iter.Pull și iterația nativă fără alocări de slice-uri intermediare.

Sintaxă
for v := range seq { ... } | next, stop := iter.Pull(seq); defer stop()

Înainte de Go 1.22, dacă aveați o colecție complexă (cum ar fi un arbore binar, o listă înlănțuită sau un flux de date) și doreați să o parcurgeți într-o buclă for ... range, erați obligat să alocați un slice intermediar complet pe heap sau să implementați canale concurente lente. Mecanismul Range-over-Func (definitivat în Go 1.23 prin pachetul iter) permite buclelor for range să itereze direct peste funcții de tip iterator cu zero alocări de memorie.

Definirea unui iterator Push cu iter.Seq2
package main

import (
    "fmt"
    "iter"
)

// Definim un arbore binar simplu:
type Nod struct {
    Valoare int
    Stanga  *Nod
    Dreapta *Nod
}

// Iterator Push (In-Order Traversal) compatibil direct cu bucla for range:
func (n *Nod) ToateValorile() iter.Seq[int] {
    return func(yield func(int) bool) {
        var parcurge func(*Nod) bool
        parcurge = func(curent *Nod) bool {
            if curent == nil {
                return true
            }
            if !parcurge(curent.Stanga) { return false }
            if !yield(curent.Valoare) { return false } // Dacă bucla dă 'break', yield returnează false!
            return parcurge(curent.Dreapta)
        }
        parcurge(n)
    }
}

func main() {
    radacina := &Nod{
        Valoare: 20,
        Stanga:  &Nod{Valoare: 10},
        Dreapta: &Nod{Valoare: 30},
    }

    // Iterație curată direct peste structura arborescentă fără alocare de memorie:
    for v := range radacina.ToateValorile() {
        fmt.Printf("%d ", v) // Afișează: 10 20 30
    }
    fmt.Println("")
}