46

It seems like every introductory document for Rust's enum types explains how to match on an enum object that you own, but what if you do not own the enum object and you just have a reference to it that you want to match against? I don't know what the syntax would be.

Here is some code where I attempt to match on a reference to an enum:

use std::fmt;
use std::io::prelude::*;

pub enum Animal {
    Cat(String),
    Dog,
}

impl fmt::Display for Animal {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Animal::Cat(c) => f.write_str("c"),
            Animal::Dog => f.write_str("d"),
        }
    }
}

fn main() {
    let p: Animal = Animal::Cat("whiskers".to_owned());
    println!("{}", p);
}

The Rust Playground gives errors on the first two cases of the match when trying to compile it:

error[E0308]: mismatched types
  --> src/main.rs:12:13
   |
12 |             Animal::Cat(c) => f.write_str("c"),
   |             ^^^^^^^^^^^^^^ expected &Animal, found enum `Animal`
   |
   = note: expected type `&Animal`
   = note:    found type `Animal`

error[E0308]: mismatched types
  --> src/main.rs:13:13
   |
13 |             Animal::Dog => f.write_str("d"),
   |             ^^^^^^^^^^^ expected &Animal, found enum `Animal`
   |
   = note: expected type `&Animal`
   = note:    found type `Animal`

How can I change that code to get it to compile? I tried adding ampersands in lots of different places without any luck. Is it even possible to match on a reference to an enum?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • It's on the [Rust roadmap](https://github.com/rust-lang/rust-roadmap/issues/24) to improve the situation here. – Bellarmine Head Nov 23 '17 at 16:45
  • And as of Rust 1.26, the situation has improved, see [Shepmaster's answer](https://stackoverflow.com/a/51111291/1026). – Nickolay Mar 10 '19 at 20:54

3 Answers3

27

Edit: Please see Shepmaster's answer for the latest idiom

The idiomatic way would be

match *self {
    Animal::Cat(ref c) => f.write_str("c"),
    Animal::Dog => f.write_str("d"),
}

You can use _ instead of ref c to silence the "unused" warning.

WiSaGaN
  • 46,887
  • 10
  • 54
  • 88
27

As of Rust 1.26, the idiomatic way is the way that you originally wrote it because match ergonomics have been improved:

use std::fmt;

pub enum Animal {
    Cat(String),
    Dog,
}

impl fmt::Display for Animal {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Animal::Cat(_) => f.write_str("c"),
            Animal::Dog => f.write_str("d"),
        }
    }
}

fn main() {
    let p: Animal = Animal::Cat("whiskers".to_owned());
    println!("{}", p);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I found a few extra subtleties by playing around: 1. You can keep `ref` in front of the inner value `c`, it will do the same as without (as long as you remove the `&` in front of the enum variant). 2. If you keep `&` in front of the enum variant `Cat` but remove `ref c`, it will try to move/copy the inner value `c`! For a borrowed content it can't move out, and will fail to compile. 3. You can also skip `&mut` and `ref mut` in `&mut Enum::Variant(ref mut value)`. You must still write `*value = new_value`. And sorry for the bad formatting due to comment limitations. – hsandt Dec 26 '18 at 16:30
  • Found an example in the Rust Book (not mut though): https://doc.rust-lang.org/book/ch15-06-reference-cycles.html#reference-cycles-can-leak-memory Listing 15-25 – hsandt Dec 26 '18 at 16:35
  • But why ? Is there an explanation for that rule ? – chylli Feb 24 '19 at 02:31
  • 1
    @chylli updated with a link to [the RFC that make the changes](https://rust-lang.github.io/rfcs/2005-match-ergonomics.html). Is that what you were looking for? – Shepmaster Feb 24 '19 at 14:45
  • What do we do if we the Dog variant is say Dog(u32), and I want to write the number to f as well in this case? – Jay Jul 09 '20 at 05:53
  • 1
    @Jay `Dog(v) => write!(f, “blah {}”, v),`. https://doc.rust-lang.org/book/ch06-02-match.html – Shepmaster Jul 09 '20 at 11:32
  • Thanks, apparently my problem was with the [or pipe](https://stackoverflow.com/questions/62819390/difficulty-aggregating-matches-with) – Jay Jul 09 '20 at 16:28
14

I figured it out thanks to helpful compiler messages:

match self {
    &Animal::Cat(ref c) => f.write_str("c"),
    &Animal::Dog => f.write_str("d"),
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
David Grayson
  • 84,103
  • 24
  • 152
  • 189