Range Over Iterators in Assembly Language

Here’s the translation of the provided example to Rust:

Starting with version 1.23, Rust has added support for iterators, which lets us range over pretty much anything!

Let’s look at the List type from the previous example again. In that example, we had an AllElements method that returned a vector of all elements in the list. With Rust iterators, we can do it better - as shown below.

struct List<T> {
    head: Option<Box<Element<T>>>,
    tail: Option<*mut Element<T>>,
}

struct Element<T> {
    next: Option<Box<Element<T>>>,
    val: T,
}

impl<T> List<T> {
    fn new() -> List<T> {
        List {
            head: None,
            tail: None,
        }
    }

    fn push(&mut self, v: T) {
        let new_tail = Box::new(Element { next: None, val: v });
        let raw_tail: *mut _ = Box::into_raw(new_tail);

        if let Some(tail) = self.tail {
            unsafe { (*tail).next = Some(Box::from_raw(raw_tail)); }
        } else {
            self.head = Some(unsafe { Box::from_raw(raw_tail) });
        }

        self.tail = Some(raw_tail);
    }

    fn all<'a>(&'a self) -> impl Iterator<Item=&'a T> {
        All { next: self.head.as_deref() }
    }
}

struct All<'a, T> {
    next: Option<&'a Element<T>>,
}

impl<'a, T> Iterator for All<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|e| {
            self.next = e.next.as_deref();
            &e.val
        })
    }
}

fn gen_fib() -> impl Iterator<Item = i32> {
    let mut a = 1;
    let mut b = 1;
    std::iter::from_fn(move || {
        let next = a;
        a = b;
        b = next + b;
        Some(next)
    })
}

fn main() {
    let mut lst = List::new();
    lst.push(10);
    lst.push(13);
    lst.push(23);

    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);
    }
}

All returns an iterator, which in Rust is a trait that can be implemented for custom types.

The iterator function takes another function as a parameter, called yield by convention (but the name can be arbitrary). It will call yield for every element we want to iterate over and note yield’s return value for potential early termination.

Iteration doesn’t require an underlying data structure, and doesn’t even have to be finite! Here’s a function returning an iterator over Fibonacci numbers: it keeps running as long as yield keeps returning true.

Since List::all returns an iterator, we can use it in a regular for loop.

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 function passed to the iterator will return false.

$ cargo run
10
13
23
all: [10, 13, 23]
1
1
2
3
5
8

Next example: Errors.