Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the Rust documentation say that a while loop over an array is slower than a for loop?

Tags:

rust

As I was reading the rust documentation, I stumbled upon this code that iterates the array a using a while loop (with an index):

fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

    while index < 5 {
        println!("the value is: {}", a[index]);

        index += 1;
    }
}

The documentation says:

... this approach is error prone; we could cause the program to panic if the index length is incorrect. It’s also slow, because the compiler adds runtime code to perform the conditional check on every element on every iteration through the loop.

The first reason was self-explanatory. The second reason was where I got confused.

Furthermore, they suggested to use a for-loop for this.

fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a.iter() {
        println!("the value is: {}", element);
    }
}

I just can't seem to wrap my head around this. Is there some kind of behavior that the Rust compiler does?

like image 713
Chubo Chan Avatar asked Sep 17 '25 16:09

Chubo Chan


1 Answers

The two parts are complementary:

we could cause the program to panic if the index length is incorrect.

Every time you write some_slice[some_index], the standard library does the equivalent of:

if some_index < some_slice.len() {
    some_slice.get_the_value_without_checks(some_index)
} else {
    panic!("Hey, stop that");
}

the compiler adds runtime code to perform the conditional check on every element

In a loop, this works out to be something like:

while some_index < limit {
    if some_index < some_slice.len() {
        some_slice.get_the_value_without_checks(some_index)
    } else {
        panic!("Hey, stop that");
    }
    some_index += 1;
}

Those repeated conditionals aren't the most efficient code.

The implementations of Iterator for slices utilize unsafe code to be more efficient at the expense of more complicated code. The iterators contain raw pointers to the data but ensure that you can never misuse them to cause memory unsafety. Without needing to perform that conditional at each step, the iterator solution is often faster1. It's more-or-less equivalent to:

while some_index < limit {
    some_slice.get_the_value_without_checks(some_index)
    some_index += 1;
}

See also:

  • Does Rust's array bounds checking affect performance?
  • Does Rust optimize for loops over calculated ranges?

1 — as Matthieu M. points out:

It should be noted that the optimizer may (or may not) be able to remove the bounds check in the while case. If it succeeds, then performance is equivalent; if it fails, suddenly your code is slower. In microbenchmarks, with simple code, changes are it will succeed... but this may not carry to your production code, or it may carry now, and the next change in the loop body will prevent the optimization, etc... In short, a while loop can be a performance ticking bomb.

See also:

  • Why does my code run slower when I remove bounds checks?
like image 103
Shepmaster Avatar answered Sep 19 '25 06:09

Shepmaster



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!