Reputation: 1990
The code below would panic because j
is typed as usize
.
fn foo(vec: &Vec<i32>) {
let mut i = 0;
let mut j = vec.len() - 1;
while i < j {
while i < j && !some_condition(vec[i]) {
i += 1;
}
while i < j && !some_condition(vec[j]) {
j -= 1;
}
if i < j {
vec.swap(i, j);
i += 1;
j -= 1;
}
}
}
foo(&vec![]);
Most of the time iterator helps to avoid such issues. But when we have to manually iterate with indexes we really need to be careful. I could check the size of vec
ahead to avoid panic. But the real problem here is I tend to think the program would work as expected even without empty check until a corner case bites me. So I wonder if there is any idiomatic way to do this kind of things in Rust.
Upvotes: 1
Views: 712
Reputation: 43
You could look at saturating_sub
or checked_sub
:
fn foo(vec: &Vec<i32>) {
let mut i = 0;
let mut j = vec.len().saturating_sub(1);
while i < j {
// Some other logic
i += 1;
// EITHER
j.saturating_sub(1); // avoid getting below 0
//OR
j = j.checked_sub(1).expect("usize underflow");
}
}
foo(&vec![]);
If you want some more complex handling instead of .expect()
you can use:
j = j.checked_sub(1).ok_or_else(|| some_your_error_creator())?;
which seems more idiomatic, but will require some additional work with error-handling.
Upvotes: 4
Reputation: 26235
Personally, I'd just do as you suggest vec.is_empty()
and then return
, to keep the logic clear.
If you don't want that, then instead you can perform a saturating subtraction using saturating_sub()
. If the subtraction would result in an underflow then it would remain at 0
.
// let mut j = vec.len() - 1;
let mut j = vec.len().saturating_sub(1);
Upvotes: 3