whyrgola
whyrgola

Reputation: 11

Use variable while rust's iter.filter closure parameter has a mutable borrow to it

In rust, when letting rust's iter.filter closure parameter capture a local variable, and I mutate that local variable in that closure, I am unable to use it at all until i finish using the iterator. It is obvious in my Minimal Reproducible Example:

fn main() {
    let my_str = "12345";

    let mut num_i_need = 1;

    let iterator = my_str
        .chars()
        .filter(|code_char| {
            // ... logic to see if `code_char` should be filtered..
            num_i_need += 1;
            true
        })
        .peekable();

    dbg!(num_i_need); // <- problem

    // Now when we loop:
    for my_char in iterator {
        dbg!(num_i_need); // <- problem
        dbg!(&num_i_need); // <- problem
    }

    dbg!(num_i_need); // <- fine
}

I hope to be able to use the variable num_i_need outside of .filter's closure (I don't intend to mutate it outside the closure) and without needing to use a Cell

I had to use a Cell to get the example to work:

use std::cell::Cell;

fn main() {
    let my_str = "12345";

    let num_i_need = Cell::new(1);

    let iterator = my_str
        .chars()
        .filter(|code_char| {
            // ... logic to see if `code_char` should be filtered..
            num_i_need.set(num_i_need.get() + 1);
            true
        })
        .peekable();

    dbg!(&num_i_need); // fine here

    // Now when we loop:
    for my_char in iterator {
        dbg!(&num_i_need); // <- NO problem, is fine
    }

    dbg!(num_i_need); // also fine
}

However in my actual codebase, I have 3 variables rather than 1 here, so I find this to be kind of undesirable and I am wondering if there is a more idiomatic way to achieve this without a Cell

Upvotes: 1

Views: 379

Answers (1)

whyrgola
whyrgola

Reputation: 11

I ended up converting my .fillter to a .scan so that it can hold state, and it just passes that state on each .next() on the iterator, to keep filtering I used a .filter after my .scan (although that is not shown in the simple example here)

fn main() {
    let my_str = "12345";

    let iterator = my_str
        .chars()
        .scan(1, |num_i_need, code_char| {
            *num_i_need += 1;
            Some((num_i_need.clone(), code_char))
        }) // you would have a `.filter` here to filter stuff out instead
        .peekable();

    // Now when we loop:
    for (num_i_need, code_char) in iterator {
        dbg!(num_i_need); // <- NO problem, is fine (Without Cell!!)
        dbg!(code_char); // (also fine)
    }
}

Upvotes: 0

Related Questions