Iter
A Rust Iterator
is analogous to the union of a Dart Iterable
and Iterator
. Since Dart already has an Iterator
class, to avoid confusion,
the Dart implementation of the Rust iterator is Iter
. Iter
makes working with collections of rust
types and regular Dart types a breeze. e.g.
List<int> list = [1, 2, 3, 4, 5];
Iter<int> filtered = list.iter().filterMap((e) {
if (e % 2 == 0) {
return Some(e * 2);
}
return None;
});
expect(filtered, [4, 8]);
Iter
can be retrieved by calling iter()
on an Iterable
or an Iterator
. Iter
can be iterated
like an Iterable
or Iterator
, and is consumed like an Iterator
.
List<int> list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
Iter<int> iter = list.iter();
List<int> collect = [];
for (final e in iter.take(5).map((e) => e * e)) {
if (e.isEven) {
collect.add(e);
}
}
expect(collect, [4, 16]);
Option<int> next = iter.next();
expect(next, Some(6));
collect.add(next.unwrap());
next = iter.next();
collect.add(next.unwrap());
expect(next, Some(7));
while(iter.moveNext()){
collect.add(iter.current * iter.current);
}
expect(collect, [4, 16, 6, 7, 64, 81]);
expect(iter,[]);
Iter
contains many more useful methods than the base Dart Iterable
class and works in all places you
would reach for an Iterator
- pub.dev
Dart vs Rust Example
Goal: Get the index of every "!" in a string not followed by a "?"
import 'package:rust/rust.dart';
void main() {
List<int> answer = [];
String string = "kl!sd!?!";
Peekable<(int index, Arr<String> window)> iter = string
.chars()
.mapWindows(2, identity)
.enumerate()
.peekable();
while (iter.moveNext()) {
final (int index, Arr<String> window) = iter.current;
switch (window) {
case ["!", "?"]:
break;
case ["!", _]:
answer.add(iter.current.$1);
case [_, "!"] when iter.peek().isNone():
answer.add(index + 1);
}
}
expect(answer, [2, 7]);
}
Rust equivalent
use std::iter::Peekable; fn main() { let mut answer: Vec<usize> = Vec::new(); let string = "kl!sd!?!"; let mut iter: Peekable<_> = string .chars() .map_windows(|w: &[char; 2]| *w) .enumerate() .peekable(); while let Some((index, window)) = iter.next() { match window { ['!', '?'] => continue, ['!', _] => answer.push(index), [_, '!'] if iter.peek().is_none() => answer.push(index + 1), _ => continue, } } assert_eq!(answer, [2, 7]); }
Additional Examples
/// Extract strings that are 3 long inside brackets '{' '}' and are not apart of other strings
String string = "jfsdjf{abcdefgh}sda;fj";
Iter<String> strings = string.runes
.iter()
.skipWhile((e) => e != "{".codeUnitAt(0))
.skip(1)
.arrayChunks(3)
.takeWhile((e) => e[2] != "}".codeUnitAt(0))
.map((e) => String.fromCharCodes(e));
expect(strings, ["abc", "def"]);
Misc
Clone
Another a big advantage of Iter
over Iterable<T>
and Iterator<T>
is that Iter<T>
is clonable.
This means the iterator can be cloned without cloning the underlying data.
var list = [1, 2, 3, 4, 5];
var iter1 = list.iter();
iter1.moveNext();
var iter2 = iter1.clone();
iter2.moveNext();
var iter3 = iter2.clone();
iter3.moveNext();
var iter4 = iter1.clone();
expect(iter1.collectList(), [2, 3, 4, 5]);
expect(iter2.collectList(), [3, 4, 5]);
expect(iter3.collectList(), [4, 5]);
expect(iter4.collectList(), [2, 3, 4, 5]);
This allows for situations where you want to work ahead and not lose your iterator position, or pass the Iter
to another function without needing to call e.g. collectList()
, collectArr()
, etc.