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?
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:
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, awhile
loop can be a performance ticking bomb.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With