ideasman42
ideasman42

Reputation: 48108

Whats the best way to write an iterator supporting multiple logic branches?

In Rust I've started writing iterators, converting them from code which took a callback function.

I ran into the problem where the code that used a callback in multiple branches of the function didn't convert so cleanly into a Rust iterator.

To give some pseudo-code.

// function using callbacks where the caller can exit at any time,
// can be used in a similar way to an iterator.
fn do_stuff(args, callback_fn(cb_args)) {
    // define a, b, c... args
    if callback_fn(a, b, 0) == false { return; }
    for i in 0..n {
        if callback_fn(c, d, i) == false { return; }
    }
    if callback_fn(e, f, -1) == false { return; }
}

Converting this to an iterator was rather awkward since I needed to store some state representing each branch.

impl Iterator for MyStruct {
    fn next(&mut self) -> Option<MyResult> {
        let out = match (self.state) {
            0 => {
                self.state += 1;
                Some(MyResult(self.a, self.b, 0))
            },
            1 => {
                self.i += 1;
                if self.i == self.n {
                    self.state += 1;
                }
                Some(MyResult(self.c, self.d, self.i - 1))
            },
            2 => {
                self.state += 1;
                Some(MyResult(self.e, self.f, -1))
            },
            _ => {
                None
            },
        }
        return out;
    }
// --- snip

With the example above, this is arguably acceptable, (if a little awkward). Consider cases with multiple for loops, variable scopes, where its much harder to track state.


While I didn't try these, I imagine there are some ways to achieve this which in most cases are less-then-ideal workarounds:

Besides the workarounds above:

Are there ways to write iterators like the example given, with less convoluted logic?
Ideally more like the example that uses callbacks.
Otherwise are there other ways to handle this?

Or is this simply not supported in Rust?


Note, the same logic applies coming from Python generators (using yield instead of a callback, using callbacks as an example here since they're ubiquitous with first class functions).

Upvotes: 4

Views: 820

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65822

Languages like C# and Python provide a way to generate iterators from methods written using a special yield keyword. As of Rust 1.11, there is no such feature in the language. However, such a feature is planned (see RFC) (indeed, yield is a reserved keyword!) and would likely work as in C# (i.e. the compiler would generate a struct with the necessary state and implementation for Iterator).

In the meantime, you could try Stateful, a project that attempts to provide this feature. (This blog post explains how Stateful works, and the challenges involved.)

Upvotes: 5

Related Questions