2 filters vs 1 filter versions - Need to confirm if the 2 filter version still runs with single loop when fold comes into action

Hi there: In the code below, with lazy iterators, is there any difference in terms of looping/performance comparing 1 filter version with 2 filters version?
Need to confirm if the 2 filter version still runs with single loop when fold comes into action. Right?

Both achieve same result as shown in Output below.

fn is_odd(n: u32) -> bool {
n % 2 == 1
}

fn is_divisible_by_5(n: u32) -> bool {
n % 5 == 0
}

fn is_odd_and_divisible_by_5(n: u32) -> bool {
is_odd(n) && is_divisible_by_5(n)
}

fn main() {
let upper = 1000;

    // 2 filters
    let filters_2_sum_of_squared_odd_numbers_divisible_by_5: u32 =
        (0..).map(|n| n * n)                             // All natural numbers squared
             .take_while(|&n_squared| n_squared < upper) // Below upper limit
             .filter(|&n_squared| is_odd(n_squared))     // That are odd
             .filter(|&n_squared| is_divisible_by_5(n_squared))     // That are divisible by 5
             .fold(0, |acc, n_squared| acc + n_squared); // Sum them
    println!("with 2 filters filters_2_sum_of_squared_odd_numbers_divisible_by_5: {}", filters_2_sum_of_squared_odd_numbers_divisible_by_5);

    // 1 filter
    let filter_1_sum_of_squared_odd_numbers_divisible_by_5: u32 =
        (0..).map(|n| n * n)                             // All natural numbers squared
             .take_while(|&n_squared| n_squared < upper) // Below upper limit
             .filter(|&n_squared| is_odd_and_divisible_by_5(n_squared))     // That are odd and  are divisible by 5
             .fold(0, |acc, n_squared| acc + n_squared); // Sum them
    println!("with 1 filter filter_1_sum_of_squared_odd_numbers_divisible_by_5: {}", filter_1_sum_of_squared_odd_numbers_divisible_by_5);
}

Output is

with 2 filters filters_2_sum_of_squared_odd_numbers_divisible_by_5: 875
with 1 filter filter_1_sum_of_squared_odd_numbers_divisible_by_5: 875

The rule of thumb is that every iterator consumer (collect, fold, simple for loop, etc.) always does one pass. Iterators usually don't store the previous states (the exception might be an explicitly written scan), they're discarded after being used, so it is not possible anyway to iterate over them again.

5 Likes

So that means there is no difference in using 2 filters vs 1 filter. When consumer loops, it is always 1 loop.
Thanks for confirming @Cerberuser

Also this related question answers why Rust does not need transducers

1 Like