Can Rust consume an iterator passed into a function?

3k views Asked by At

I'm trying to implement a simple REPL calculator in Rust and I'm hitting brick walls all over the place.

I'm consuming chars while iterating over a hardcoded string. When I hit a numeric character I want to pass control over to a function that will consume the rest of the number (assuming the number has more than one digit) and return the number, converted to an Integer.

I'm having trouble with passing Chars iterator to a function. The error I'm getting is use of moved value: 'iter'.

I understand that I can't mutate something that I gave to someone else - something that had its ownership moved - but I don't know any other way of doing this, especially since the Chars iterator is non-copyable.

#[derive(Clone, Debug)]
enum Token {
    Addition,
    Substraction,
    Multiplication,
    Division,
    Integer(i32),
    Error,
}

fn consume_number(mut iter: std::str::Chars) -> Option<i32> {
    while let Some(item) = iter.next() {
        println!("{:?}", item);
    }

    return Some(1337);
}

fn tokenize(line: &str) -> Vec<Token> {
    let mut iter = line.chars();
    let mut tokens = Vec::new();
    let mut token;

    while let Some(c) = iter.next() {
        if c.is_whitespace() { continue };

        if c.is_digit(10) {
            token = match consume_number(iter) {
                Some(i32) => Token::Integer(i32),
                None => Token::Error,
            };
        } else {
            token = match c {
                '+'                    => Token::Addition,
                '-'                    => Token::Substraction,
                '*'                    => Token::Multiplication,
                '/'                    => Token::Division,
                _                      => Token::Error,
            };
        };
        tokens.push(token);
    }
    return tokens;
}



fn main() {
    let line = "631 * 32 + 212 - 15 / 89";
    println!("{:?}", tokenize(&line));
}
1

There are 1 answers

4
Matthieu M. On BEST ANSWER

The answer is yes, it's done in the FromIterator trait.

What you experience here is much more basic:

fn consume_number(mut iter: std::str::Chars) -> Option<i32> { ... }

while let Some(c) = iter.next() {
    ...
    match_consume_number(iter)
    ...
}

When calling match_consume_number you are transferring ownership of the iterator to it. It means that at the next iteration of the loop body, this iter variable is no longer available.

If the iterator is meant to still be usable afterward, you should pass a reference to it:

fn consume_number(iter: &mut std::str::Chars) -> Option<i32> { ... }

while let Some(c) = iter.next() {
    ...
    match_consume_number(&mut iter)
    ...
}

You were close!