Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot change one value of an enum because it's a re-assignment of an immutable variable

Tags:

enums

rust

I have enums that contain variables:

enum Asymmetric {
    One(i32),
    Two(i32, i32),
}

I want to change just one field of an already existing enum, without reassigning the entire enum. My code (playground):

// Does not compile
fn main() {
    let two = Asymmetric::Two(4, 5);
    let mut vec = vec![two];
    foo(&mut vec[0]);
}

fn foo(baa: &mut Asymmetric) {
    match baa {
        &mut Asymmetric::Two(x0, x1) => {
            x0 = 6;
        }
        _ => {}
    }
}

This results in this error:

error[E0384]: re-assignment of immutable variable `x0`
  --> src/main.rs:16:13
   |
15 |         &mut Asymmetric::Two(x0, x1) => {
   |                              -- first assignment to `x0`
16 |             x0 = 6;
   |             ^^^^^^ re-assignment of immutable variable
like image 812
HiDefender Avatar asked Dec 07 '22 20:12

HiDefender


1 Answers

Thanks to "match ergonomics" (introduced in Rust 1.26, proposed here), you can write your code like this:

fn foo(baa: &mut Asymmetric) {
    match baa {
        Asymmetric::Two(x0, _) => {
            *x0 = 6;
        }
        _ => {}
    }
}

Since baa is a mutable reference, but your pattern you're matching against (Asymmetric::Two(x0, _)) is not, the name x0 is automatically bound as mutable reference.

You can also do it manually by using ref mut. See this working code (playground):

fn foo(baa: &mut Asymmetric) {
    match *baa {
        Asymmetric::Two(ref mut x0, _) => {
            *x0 = 6;
        }
        _ => {}
    }
}

Some minor changes that are not related to your error, but which increase your code quality:

  • usually you deref (with *) the matched-on value instead of adding & or &mut to every pattern in the match
  • you should use _ as a name placeholder if you don't need to bind to that name

In your case, you can simplify the code even further by using if let. Whenever you are only interested in one match-case, you should use if let instead:

fn foo(baa: &mut Asymmetric) {
    if let Asymmetric::Two(x0, _) = baa {
        *x0 = 6;
    }
}
like image 67
Lukas Kalbertodt Avatar answered Jan 05 '23 00:01

Lukas Kalbertodt