steveklabnik, along these same lines, I've come across the claim that things like map, reduce, fold, etc., are not idiomatic Rust. But nothing about why or why not that might be true. That kind of guidance would be valuable, especially since Rust borrows so many concepts from functional languages.
I will certainly say that Rust is young enough that idioms are still forming, so beware of those who say anything is strongly idiomatic. Except four spaces, no tabs. That's _incredibly_ so. ;)
But yes, as they develop, I'll be encouraging the use of idioms directly in the documentation. My years of Ruby make me a big believer in developing and following idioms.
Oh, one last thing about that: iterators are way better than explicit loops in Rust, because loops have to do bounds checking, but iterators don't. So generally, iterators are not just better stylistically, but also performance-wise too.
> because loops have to do bounds checking, but iterators don't.
They still have to check something to know when to stop iterating, though, don't they? You may have traded a bounds check for a dynamic dispatch or some "is done" check, but there's still some conditional logic each iteration isn't there?
That is, "an iterator" is an object that has a next method yielding an optional value, with a missing value indicating the end of iteration. This is conventionally used statically dispatched, and the next method will normally be inlined, so the compiler can optimise it well.
(In particular iterators over simple data structures like vectors optimise to the same code as the equivalent in C/C++, modulo one small bug where LLVM doesn't yet fully understand the nonnullability of Rust's references.)
pcwalton or someone else who understands the issue more deeply here, but my current understanding is this:
Imagine we want to print out every single element of a `Vec`, Rust's growable array type. Here's the code with a loop:
fn main() {
let v = vec!(1, 2, 3);
for i in range(0, v.len()) {
println!("Number {}", v.get(i));
}
}
This of course, has to check that it's only iterated the maximum number of times. The check I'm referring to, though, is in `v.get`. That has to do a bounds check on the array. If we use the iterator version...
fn main() {
let v = vec!(1, 2, 3);
for i in v.iter() {
println!("Number {}", i);
}
}
Now we get each element out of the vector. No more bounds check!
The other replies explained the situation, but for the curious here's Rust's basic code for slice iteration: https://github.com/rust-lang/rust/blob/master/src/libcore/sl... (unfortunately buried in some macros). `if self.ptr == self.end` is where the guarantee is made, then the offsets are done unsafely. The other branch in that function is optimized away statically.
Bounds checking is different from the conditional checks in something like a loop. It's the logic that prevents you from accessing memory that isn't yours. If you have an array arr of length 5 in Java, if you do arr[10] you get an IndexOutOfBoundsException because you're not allowed to do that. If you did the same thing in C, you might get some data which is unrelated to your array, or perhaps a segfault.
So when you you loop over an array, your loop conditional makes sure that your index hasn't indexed out of bounds, in addition to the bounds checking that is performed when actually indexing the array. So you in a sense pay a double price for the same assurance. (This is modulo compiler optimizations.)
Rust has to guarantee memory safety (I guess Java makes the same promise). This means that you have to have bounds checking for array indexing, lest you index out of bounds of some array and get killed by the operating system, if that memory happens to be outside of your process' "turf". You can't eliminate that in general (or; it's hecka hard), but you can probably encapsulate things like iterating over data structures like arrays, vet the code, and say that "this thing loops from 0 to the length of the array minus 1... I swear on my mother's future grave that it is safe to turn off bounds checking for array indexing here." (The way they do it in Rust may be more rigorous than this, for all I know!)
> I swear on my mother's future grave that it is safe to turn off bounds checking for array indexing here." (The way they do it in Rust may be more rigorous than this, for all I know!)
The hard part about doing that is that it's only safe if the array is guaranteed not to be mutated during the loop. Rust enforces this through its borrow checking system.
Agreed, and I plan on explicitly tackling this too. That'll come after Cargo is a bit more solid, however.