Iteradores Personalizados
En esta página
El for x in expr de Zolo se impulsa mediante dos traits de core::iter:
Iterator<T>— un cursor con estado;next(self) -> T?lo avanza. Devolvernilseñala agotamiento.IntoIterator<T>— un contenedor que puede producir un cursor fresco viaiter(self).for x in containerllama aiter()automáticamente.
Cualquier tipo que implemente alguno de estos traits se conecta a for sin
código adicional.
Cursor personalizado con for, un contenedor IntoIterator, enumeración
for i, x, y cadena de adaptadores.
// Feature: Custom iterators
// Traits: `Iterator<T>` (cursor) and `IntoIterator<T>` (producer)
// Works on: VM, native, wasm-aot
// Note: adapter chains (.map/.filter/.sum/…) work on VM only.
// `Iter` (the adapter type returned by `iter()`) lives in std.
use std::Iter
// ── 1. Iterator<T> — direct cursor ──────────────────────────────────────────
//
// Implement `Iterator<T>` to make a struct driveable by `for x in`.
// The single required method is `next(self) -> T?`; returning `nil` signals
// the end of the sequence.
struct Countdown { n: int }
impl Iterator<int> for Countdown {
fn next(self) -> int? {
if self.n <= 0 { return nil }
self.n = self.n - 1
return self.n + 1
}
}
// ── 2. IntoIterator<T> — container that produces a cursor ───────────────────
//
// Implement `IntoIterator<T>` when the iterable thing is a *container*.
// `iter()` returns a fresh cursor; `for x in container` calls `iter()` first.
struct Bag { items: [int] }
impl IntoIterator<int> for Bag {
fn iter(self) -> Iter<int> { return Iter.from(self.items) }
}
fn main() {
// for loop over a direct Iterator
let c = Countdown { n: 3 }
for x in c { print(x) }
// expected: 3
// expected: 2
// expected: 1
// Manual sum via for loop (works on all backends)
var total = 0
let c2 = Countdown { n: 3 }
for x in c2 { total = total + x }
print(total)
// expected: 6
// IntoIterator container
let b = Bag { items: [10, 20, 30] }
for x in b { print(x) }
// expected: 10
// expected: 20
// expected: 30
// for i, x enumerate (0-based index)
let arr = [100, 200, 300]
for i, x in arr { print(i) print(x) }
// expected: 0
// expected: 100
// expected: 1
// expected: 200
// expected: 2
// expected: 300
// Adapter chain on custom iterator — VM only
// (on native/wasm-aot use the for-loop form above)
let c3 = Countdown { n: 5 }
let mapped_sum = c3.map(|x| x * 2).filter(|x| x > 4).sum()
print(mapped_sum)
// expected: 24
}
Iterator<T> — cursor directo
struct Countdown { n: int }
impl Iterator<int> for Countdown {
fn next(self) -> int? {
if self.n <= 0 { return nil }
self.n = self.n - 1
return self.n + 1
}
}
fn main() {
let c = Countdown { n: 3 }
for x in c { print(x) } // 3, 2, 1
}
IntoIterator<T> — contenedor
struct Bag { items: [int] }
impl IntoIterator<int> for Bag {
fn iter(self) -> Iter<int> { return Iter.from(self.items) }
}
fn main() {
let b = Bag { items: [1, 2, 3] }
for x in b { print(x) } // 1, 2, 3
}
for x in b se expande llamando a b.iter() y conduciendo el cursor
devuelto.
for i, x — enumeración (base 0)
let arr = [10, 20, 30]
for i, x in arr { print(i) print(x) }
// 0 10 1 20 2 30
Funciona con arrays, contenedores IntoIterator y literales de mapa (donde
i es la clave y x el valor).
Cadenas de adaptadores — solo VM
En la VM, un iterador de usuario puede envolverse en el adaptador de la
prelude Iter<T>, dando acceso a la suite completa
map/filter/take/fold/sum/collect/…:
let result = Countdown { n: 5 }
.map(|x| x * 2)
.filter(|x| x > 4)
.sum()
// 24
Limitación de backend: las cadenas de adaptadores (.map().filter().sum())
y la desestructuración for k, v in mapLiteral funcionan solo en la VM.
En native (Cranelift) y wasm-aot estos backends carecen del runtime del
adaptador Lua Iter.*. Usa la forma de bucle for cuando necesites
portabilidad entre backends.
Bounds genéricos
Una función puede aceptar cualquier Iterator<T> mediante un bound genérico:
fn sum_all<I: Iterator<int>>(it: I) -> int {
var total = 0
for x in it { total = total + x }
return total
}
Iterator es un trait conocido; el bound es aceptado por el compilador.
Limitación en tiempo de ejecución — usa
fordentro de cuerpos genéricos. Llamar a métodos adaptadores (.fold/.map/…) directamente sobre un valor genéricoI: Iteratorfalla en tiempo de ejecución en todos los backends. El parámetro genéricoites una struct de usuario, no un envoltorioIter, por lo querawgetno encuentra ningún slot de adaptador en él. Usa siempre un buclefor x in it { … }dentro del cuerpo de una función genérica.
Nota: la verificación del tipo de elemento en los sitios de llamada (verificar que
I es específicamente Iterator<int> y no Iterator<str>) es una mejora
conocida para el futuro.
El centinela nil
next() devuelve T? — nullable. El centinela de fin de secuencia es nil.
Esto significa que una secuencia cuyos elementos legítimos pueden ser nil
no puede iterarse con este protocolo. Diseña tales secuencias con un tipo
envolvente.
Errores
| Código | Significado |
|---|---|
| TE828 | for x in expr cuando el tipo de expr no implementa Iterator ni IntoIterator. |