Iteradores Personalizados
Nesta página
O for x in expr do Zolo é dirigido por dois traits de core::iter:
Iterator<T>— um cursor com estado;next(self) -> T?o avança. Retornarnilsinaliza esgotamento.IntoIterator<T>— um container que pode produzir um cursor fresco viaiter(self).for x in containerchamaiter()automaticamente.
Qualquer tipo que implemente algum desses traits se conecta ao for sem
código adicional.
Cursor personalizado com for, um container IntoIterator, enumeração
for i, x, e cadeia 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 direto
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> — container
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 chamando b.iter() e conduzindo o cursor retornado.
for i, x — enumeração (base 0)
let arr = [10, 20, 30]
for i, x in arr { print(i) print(x) }
// 0 10 1 20 2 30
Funciona com arrays, containers IntoIterator e literais de mapa (onde i
é a chave e x o valor).
Cadeias de adaptadores — somente VM
Na VM, um iterador de usuário pode ser envolto no adaptador da prelude
Iter<T>, dando acesso à suite completa
map/filter/take/fold/sum/collect/…:
let result = Countdown { n: 5 }
.map(|x| x * 2)
.filter(|x| x > 4)
.sum()
// 24
Limitação de backend: cadeias de adaptadores (.map().filter().sum()) e
a desestruturação for k, v in mapLiteral funcionam somente na VM. No
native (Cranelift) e wasm-aot esses backends não possuem o runtime do
adaptador Lua Iter.*. Use a forma de laço for quando precisar de
portabilidade entre backends.
Bounds genéricos
Uma função pode aceitar qualquer Iterator<T> via um 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 é um trait conhecido; o bound é aceito pelo compilador.
Limitação em tempo de execução — use
fordentro de corpos genéricos. Chamar métodos adaptadores (.fold/.map/…) diretamente sobre um valor genéricoI: Iteratorfalha em tempo de execução em todos os backends. O parâmetro genéricoité uma struct de usuário, não um wrapperIter, portantorawgetnão encontra nenhum slot de adaptador nele. Sempre use um laçofor x in it { … }dentro do corpo de uma função genérica.
Nota: a verificação do tipo de elemento nos sites de chamada (verificar que I é
especificamente Iterator<int> e não Iterator<str>) é uma melhoria
conhecida para o futuro.
O sentinela nil
next() retorna T? — anulável. O sentinela de fim de sequência é nil.
Isso significa que uma sequência cujos elementos legítimos podem ser nil
não pode ser iterada com este protocolo. Projete tais sequências com um tipo
invólucro.
Erros
| Código | Significado |
|---|---|
| TE828 | for x in expr quando o tipo de expr não implementa Iterator nem IntoIterator. |