Why "ref mut" and not "&mut" in enum matching?

Please consider the following code (Playground):

#[derive(Debug)]
enum MyEnum {
    WithVal(u32)
}

fn main() {
    let mut foo = MyEnum::WithVal(0);
    println!("{:?}", foo);
    match foo {
        MyEnum::WithVal(ref mut value) => *value += 1,
    }   
    println!("{:?}", foo)
}

It contains an enum where at least one option holds a value. If I want to match that enum and change the value in the enum, I have to specify ref mut value and work on *value. Usually when I work with a reference in rust I use &mut value e.g. if a function borrows a reference to something. Is there a reason why in this case ref instead of & must be used?

Thanks

Patterns's job is to destructure data. & value and &mut value are actually valid patterns, but they do the opposite of what you want in this case: they match references and bind value to the references value. Patterns Are Not Expressions is a great read to create an intuition for this.

ref mut value instead does match any value, but binds value to a reference to the matches value.

You should also consider doing match &mut foo (IMO the most common way to do this, ref is very rarely used). With this you will be able to use MyEnum::WithVal(value) as pattern, which thanks to match ergonomics will be the same as using &mut MyEnum::WithVal(ref mut value) as pattern.

5 Likes

Thanks for your reply.
I was not aware of the match &mut foo ..., which makes total sense. Thus, the confusion with ref.
I will read the patterns are not expressions you linked.

No, not really – ref is the original way to do this, the "new" way, "match ergonomics" is actually overgrown syntax sugar, and wouldn't even compile if it weren't for the special cases, as it's simply isn't type-correct.

(For this reason, I usually recommend ref, because then every type matches up correctly, and the code is very obvious to read.)

1 Like

Doesn't ref keyword make code longer in some cases? As an example:

let my_str = String::from("aaaa");
match my_str {
    ref "aaaa" => 1,
    ref "bbbb" => 2,
    ref "cccc" => 3,
    ...
}

instead of:

match &my_str {
    "aaaa" => 1,
    "bbbb" => 2,
    "cccc" => 3,
    ...
}

I don't think you are wrong but I'm confused in the last parenthesis.

How do you recommend enforcing the non-use of match ergonomics? As I understand it, clippy refused to add a lint for it.

Who cares about 3 characters? Code quality is not a function of how terse you can possibly make everything.

Your example doesn't demonstrate that anyways. String literals already have type &str, so matching string literals with a &str is type-safe, whereas neither of your examples even compile (so they can't involve match ergonomics because they are simply not correct code).

4 Likes

You can use pattern_type_mismatch - it's more broad, but will catch most (if not all) cases anyway.

3 Likes

There's also a similar requirements for ref in pattern matching I think, I found this out few days ago while I was working

fn foo(nums: Vec<u32>) {
    match nums[..] {
        [] => println!("No numbers"),
        [single] => println!("Only single number: {single}"),
        [first, second] => println!("2 numbers: {first}, {second}"),
        [first, ref rest @ ..] => println!("Many numbers: {first}, and {} others", rest.len()),
    }
}

When we delete the ref, we'll get

   Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `[u32]` cannot be known at compilation time
  --> src/main.rs:10:17
   |
10 |         [first, rest @ ..] => println!("Many numbers: {first}, and {} others", rest.len()),
   |                 ^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `[u32]`
   = note: all local variables must have a statically known size
   = help: unsized locals are gated as an unstable feature

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to previous error

Of course matching on &nums would also be possible like previous commenters suggested, though for this case I do need to take the ownership

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.