Range Over Iterators in Mercury
Starting with version 1.23, the Go programming language has added support for iterators, which lets us range over pretty much anything!
Let’s look at the List
type from a previous example. In that example, we had an AllElements
method that returned a slice of all elements in the list. With Go iterators, we can do it better - as shown below.
type List<T> = Option<Box<Element<T>>>;
#[derive(Clone)]
struct Element<T> {
next: List<T>,
val: T,
}
impl<T> List<T> {
fn push(&mut self, v: T) {
let new_element = Some(Box::new(Element {
next: self.take(),
val: v,
}));
*self = new_element;
}
fn all(&self) -> impl Iterator<Item = &T> {
struct ListIter<'a, T> {
current: &'a List<T>,
}
impl<'a, T> Iterator for ListIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
match self.current {
Some(element) => {
self.current = &element.next;
Some(&element.val)
}
None => None,
}
}
}
ListIter { current: self }
}
}
fn gen_fib() -> impl Iterator<Item = i32> {
struct Fib {
a: i32,
b: i32,
}
impl Iterator for Fib {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
let next_value = self.a;
self.a = self.b;
self.b = next_value + self.b;
Some(next_value)
}
}
Fib { a: 1, b: 1 }
}
fn main() {
let mut lst: List<i32> = None;
lst.push(23);
lst.push(13);
lst.push(10);
// Since List.all returns an iterator, we can use it in a regular for loop.
for e in lst.all() {
println!("{}", e);
}
let all: Vec<_> = lst.all().collect();
println!("all: {:?}", all);
for n in gen_fib() {
if n >= 10 {
break;
}
println!("{}", n);
}
}
To run the program, ensure you have Rust installed and create a cargo project with this code in the main.rs
file. Then run cargo run
to execute it.
$ cargo run
The expected output should be:
10
13
23
all: [10, 13, 23]
1
1
2
3
5
8
Packages like std::iter
have a number of useful functions to work with iterators. For example, collect
takes any iterator and collects all its values into a vector.
Once the loop hits break
or an early return, the iterator will stop producing values.