Modifying vector value according conditions - idiomatic way?

Hi, so this might be a quite newbie question, but nevertheless I did not find sensible answer. Given that I'm given a vector of values v, let's assume 5 of them. I need to modify the middle one based on conditions regarding the whole array. A pseudo code would look like this:

let mut v = vec![Some(1), None, Some(2), Some(3), None];
    match v.get_mut(2){
        Some(&mut Some(ref mut x)) => {
            match v.get(0) {
                Some(& Some(ref y)) => {
                    if *y < 10{
                        *x = v.get(3).unwrap().unwrap() + 5;
                    } else {
                        *x = v.get(3).unwrap().unwrap() + 2;
                    }
                },
                _ => ()
            }
        },
        _ => ()
    }

Of course this will not compile for the obvious reasons of borrowing. My question is, what is the proper way to this in rust as I could not figured it out myself so far. I thought of 3 possible solution, but I think both are ugly:

  1. For each match copy the value from the vector and use that inside the match, so that you are not really borrowing v
  2. Make boolean variables which describe which path you take, use the match with borrows only to set these variables
    The third solution is sort of presented below:
let mut v = vec![Some(1), None, Some(2), Some(3), None];
    if match v.get(2){
        Some(&Some(_)) => true,
        _ => false
    } && match v.get(0) {
        Some(&Some(ref y)) => true,
        _ => false
    } {
        v[2] = if v.get(0).unwrap().unwrap() < 10 {
            Some(v.get(3).unwrap().unwrap() + 5)
        } else {
            Some(v.get(3).unwrap().unwrap() + 2)
        };
    };

So I after all this I wanted to know if you guys have any suggestions for better ways, and how to think about this in rust. Cheers!

I think that instead of using Vec::get you can use [] operator here:

fn main() {
    let mut v = vec![Some(1), None, Some(2), Some(3), None];
    if v.len() >= 4 {
        v[2] =  {
            let increment = if v[0].map(|x| x < 10).unwrap_or(false) { 5 } else { 2 };
            Some(v[3].unwrap() + increment)
        };
    }
}

Oh, so does the [] operator perform a copy then? Or otherwise why it is not borrowing the vector?

[] returns a reference. There is no implicit non trivial copying in Rust at all. I think that when you do

v[i] = calculate(v[i], v[j], v[k])

then you don't need to borrow v mutably while you are performing calculate. In other words, this can be rewritten as

let new_value = {
    // borrow `v` immutably several times
    let increment = if v[0].map(|x| x < 10).unwrap_or(false) { 5 } else { 2 };
    Some(v[3].unwrap() + increment)
}; 

// immutable borrows of `v` end

v[2] = new_value;  // borrow `v` mutably. 
1 Like

Ok I think I understood. To some extend, and please correct me if wrong, actually my main problem was that the match makes a immutable borrow of v which prevents me from mutably changing it. So in fact very similar to your code, but with using the get for checks, this compiles:

v[2] = match v.get(2) {
        Some(&Some(val1)) => match v.get(0) {
              Some(&Some(val2)) => if val2 < 10{Some(val1 + 5)} else {Some(val2 + 2)},
            _ => v[2]
        },
        _ => None
    };

I think I get it now from your explanation, the proper way is to first compute the value in a separate scope and then assign it, thus destorying all of the borrows needed for the computation inside that scope.

Yest, that is basically it, except that the borrow in match v.get_mut(2) is mutable, so it prevents all subsequent borrows.