I have a chain of iterator adapters (which I don't know the number of at compile time) applied to an initial iterator. A simplified example would be to filter a range of numbers by their divisibility and obtain the number of remaining numbers at the end:
let n: usize = 10;
let mut iter_chain: Box<dyn std::iter::Iterator<Item = usize>> = Box::new(1..=n);
for i in 2..n {
iter_chain = Box::new(iter_chain.filter(move |j| j % i != 0));
}
println!("{}", iter_chain.count());
Now, I want to count the numbers of elements in each stage of this filter chain.
My idea is to insert between each filter a inspect
adapter that increases a counter for the corresponding stage.
However, each inspect
adapter has to borrow the mutable counter from somewhere outside of the loop.
This leads to multiple mutual borrows of the Vector in which I store the counters at the moment.
let n: usize = 10;
let mut iter_chain: Box<dyn std::iter::Iterator<Item = usize>> = Box::new(1..=n);
let mut filter_overview = vec![0; n];
for i in 2..n {
// cannot borrow `filter_overview` as mutable more than once at a time
// |
// V
iter_chain = Box::new(iter_chain.inspect(|_| filter_overview[i] += 1));
iter_chain = Box::new(iter_chain.filter(move |j| j % i != 0));
}
println!("{filter_overview:?} {}", iter_chain.count());
The expected values of filter_overview
in this example are [10, 5, 3, 3, 2, 2, 1, 1]
.
Is there a workaround for this problem, or do I have to use something different than a Vector to store these counters? Maybe there is an entirely different way to achieve this?
Instead of using interior mutability, you can do that by iterating over filter_overview
. You need to be careful to declare it before iter_chain
, however, otherwise it will be used after it's freed:
let n: usize = 10;
let mut filter_overview = vec![0; n];
let mut iter_chain: Box<dyn std::iter::Iterator<Item = usize>> = Box::new(1..=n);
for (i, overview) in std::iter::zip(2..n, &mut filter_overview) {
iter_chain = Box::new(
iter_chain
.inspect(|_| *overview += 1)
.filter(move |j| j % i != 0),
);
}
println!("{filter_overview:?} {}", iter_chain.count());