rust

How to idiomatically loop over an array of variables, and mutate them?


I want to loop over a fixed set of variables, and initialise each one according to the output of an iterator.

In my example, the input string 'dimensions' represents package dimensions, in the form e.g. "32x13x16". I am splitting on 'x' to get length, width, and height. I know there will always be exactly three dimensions in the input. I make a list of my three variables and I assign them to the three outputs of my iterator & parser.

let mut l: u32 = 0;
let mut w: u32 = 0;
let mut h: u32 = 0;

let mut dimensions = dimensions.split("x");

for dim in [&mut l, &mut w, &mut h].iter_mut() {
    match dimensions.next() {
        Some(x) => {
            let num: u32 = x.parse().expect("dimensions should be integers");
            **dim = num
        }
        None => break,
    }
}

My code works, but it seems over-complicated to me. I have to double-deference the variable in order to assign it. I cannot find a way to declare the variables or the loop such that there is only one layer of dereferencing, which the compiler will accept. Shouldn't I be able to write it in a way where it's just one level of reference?

Also, the compiler requires me to assign initial values to my l, w, and h variables before I use them, but I am going to overwrite them immediately. If I do not initialise my variables, the compiler errors. Is there a way I can avoid this?

In general is there a more idiomatic way I can achieve this?


Solution

  • My code works, but it seems over-complicated to me. I have to double-deference the variable in order to assign it - shouldn't I be able to write it in a way where it's just one level of reference?

    You only need iter_mut to get mutable references to the contents of an iterator. In your case the iterator already contains mutable references, so no need to get &mut &mut T.

    for dim in [&mut l, &mut w, &mut h] {
        match dimensions.next() {
            Some(x) => {
                let num: u32 = x.parse().expect("dimensions should be integers");
                *dim = num
            }
            None => break,
        }
    }
    

    Also, the compiler requires me to assign initial values to my l, w, and h variables before I use them, but I am going to overwrite them immediately - is there a way I can avoid this?

    If you cannot statically prove that every variable will have been assigned — which you can't, because you can't ensure that dimensions has at least three components — then Rust will correctly assume the variables might be unassigned.

    However, you can do something like this:

    let [l, w, h] = [(); 3].map(|_| {
        dimensions
            .next()
            .and_then(|s| s.parse::<u32>().ok())
            .expect("dimensions should be integers")
    });