|
|
|
|
|
by gobdovan
16 days ago
|
|
Both rewrites differ semantically from: for i in 0..32 { a[i] = 1; } If a.len() == 16, the indexed loop writes a[0]..a[15] and then panics at a[16]. By contrast, both assert!(a.len() >= 32); and a[0..32].iter_mut().for_each(|el| *el = 1) fail before any writes occur. The former at the explicit assertion, the latter while creating the a[0..32] subslice. That difference is observable if the panic is caught, and the panic location/message may also differ. This is why these are valid manual rewrites only when the intended precondition is "the slice has length at least 32," not generally valid compiler rewrites of the original loop. The GitHub issue discussion is directly about these concerns and discuss whether bounds checks may fail early, whether intermediate writes are observable after catch_unwind and whether panic behavior must be preserved. |
|
No argument about the point of the issue. But this is a discussion about the relative efficiency of C, C++ and Rust. My point is there is a way in Rust to say "I don't care about observable writes, hoist the bounds check out of the loop", so that the efficiency is the same.
Admittedly, it's not part of the language definition. You're relying on intimate knowledge of how the optimiser works. In fact, you are probably pasting the code into godbolt, and looking at the assembler produced. But if you care about cycles that much, that's true for all three languages.