I'm a fairly advanced programmer (experience in Java, Python, C#, C, C++) who is now trying to learn Rust for the first time. Rust is quite different from any language I've tried before, so I'm struggling quite a bit. To get to know the language I'm implementing a little game.
At some point in my code, I would like to access an element from my 'board' and a surrounding element. If my element is at the edge of the board (index 0 or index max), I want a clipped value (eg index 0 - 1 == 0) If I simplify this use case, I'm trying to access an index from an array and a relative index. Below is a very minimal example:
fn get_elements(array: &Vec<char>, i: usize, dx: i8) -> (char, char) {
let center = array[i];
let other = array[i+dx];
(center, other)
}
fn main() {
let data = vec!['a', 'b', 'c', 'd', 'e'];
let (c, o) = get_elements(&data, 2, -1);
}
As expected, this fails to compile because no implementation for `usize + i8`
.
I understand this is because the behavior of subtracting below zero is undefined for unsigned numbers.
However, it is unclear to me how I can achieve the wanted behavior in Rust.
There’s usize::saturating_add_signed
:
fn get_elements(array: &[char], i: usize, dx: isize) -> (char, char) {
let center = array[i];
let other = array[i.saturating_add_signed(dx).min(array.len() - 1)];
(center, other)
}
Note that I changed dx
to isize
for convenience (isize::from(dx)
= dx.into()
will work if you want to keep it i8
) and array
to &[char]
because you don’t need the Vec
ness.
Alternatively, you might find options like the following where the index passes through isize
more readable. The only behavior difference is for usize
s that don’t fit in isize
, which it wouldn’t be unreasonable to consider out of scope in most cases.
let len: isize = array.len().try_into().unwrap();
let center = array[i];
let other = array[(i as isize + dx).clamp(0, len - 1) as usize];
(center, other)
I understand this is because the behavior of subtracting below zero is undefined for unsigned numbers.
Subtracting below zero is actually well-defined to panic in debug builds and wrap in release, but it’s about being explicit, yes.