rustunsigned-integerinteger-arithmetic

rust relative array indexing


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.


Solution

  • 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 Vecness.

    Alternatively, you might find options like the following where the index passes through isize more readable. The only behavior difference is for usizes 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.