njaard
njaard

Reputation: 549

Nesting an iterator's loops

As you know, a for in loop owns its iterator for the duration of the loop if you pass it an iterator directly, like so:

let v = vec![...];
let mut i = v.iter();
for _ in i { }

As malbarbo observes, you can instruct for to take a reference to i by writing i.by_ref(). However, you can't repeat that from inside the for loop:

for _ in i.by_ref() {
    for _ in i.by_ref() {
          // ^ error: cannot borrow `i` as mutable
          // more than once at a time [--explain E0499]
        break;
    }
}

Understandably, the outer for loop must modify its iterator, so it takes a mutable reference to it, and nobody else can call mutable methods on i anymore. We can show this problem more directly like so:

for _ in i.by_ref() {
    i.next(); // same error
}

One recourse is to make the outer for a loop and call i.next() directly. Is there a prettier way to have our cake (the outer for loop iterates over i) and eat it too (we can still advance i inside the outer loop)?

Upvotes: 4

Views: 1845

Answers (1)

Linear
Linear

Reputation: 22226

This is doable using a while let expression.

let x = vec![1, 2, 3, 5, 4, 6, 7, 5, 8, 5];

let mut i = x.iter();
while let Some(v) = i.next() {
    println!("First before inner loop: {}", v);
    for v in i.by_ref() {
        if *v == 5 {
            println!("Found a 5");
            break;
        }
    }
}

Playground

while let Some(v) = i.next() is more or less directly equivalent to "for loop without borrowing the iterator". It, in turn, is essentially just:

loop {
    match i.next() {
        Some(v) => { loop_body },
        _ => { break; },
    };
}

Bonus: you can use while let on pretty much any other expression you want to call repeatedly that returns a sum type.

Upvotes: 5

Related Questions