6

As a newcomer to Rust, I've stumbled upon two apparently valid ways of running a match on a reference type.

I've defined an enum:

enum Color {
    Red,
    Yellow,
    Green,
    Teal,
    Blue,
    Purple,
}

I want to implement a function that works on a &self reference of an instance of this enum.

I can see two ways to write such a function:

impl Color {
    // Approach #1: Match the reference, using references in each pattern
    fn contains_red(&self) -> bool {
        match self {
            &Color::Red => true,
            &Color::Yellow => true,
            &Color::Purple => true,
            _ => false,
        }
    }

    // Approach #2: Dereference &self and match the patterns directly
    fn contains_blue(&self) -> bool {
        match *self {
            Color::Blue => true,
            Color::Teal => true,
            Color::Purple => true,
            _ => false,
        }
    }
}

I expected that dereferencing &self would be counted as a move, and would cause errors if I called color.contains_blue() on the same instance twice in a row, but this doesn't seem to be the case.

Are these approaches functionally identical? Would one of them break down if I were matching more complex objects?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
KChaloux
  • 3,918
  • 6
  • 37
  • 52
  • 2
    I've been told to dereference the match argument (your approach #2) as it's more idiomatic because it has less clutter. I hope someone can chime in with a more authoritative answer. :-) – Shepmaster Mar 10 '16 at 15:46
  • Just to make sure: `Color` is not `Copy`, right? – Matthieu M. Mar 10 '16 at 15:57
  • @Matthieu Nope, it's defined as seen above, with no attributes. But if that makes a significant difference as to the behavior, I'd love to hear about it. – KChaloux Mar 10 '16 at 15:58
  • Yep, I'm also pretty sure that the second approach is the idiomatic one, but I can't give any links supporting this claim as well :) And whether the matched value is `Copy` or not also does not make difference, I believe. – Vladimir Matveev Mar 10 '16 at 16:24
  • @Shepmaster Is the style guide authoritative enough? https://doc.rust-lang.org/style/features/match.html (OK, not quite the same, but still ...) – starblue Mar 10 '16 at 16:35
  • @starblue I think that would be a fine answer. It's at least an opinion that comes from the creators of Rust, who ought to know how to use their language. I'd still love to know if there's a real functional difference between the two approaches, however, or if it really is just a style thing. – KChaloux Mar 10 '16 at 17:25

3 Answers3

8

You can't move out of an immutable reference, so you don't have to worry about that being the case.

It's worth considering that one would expect matches to work somewhat akin to (Partial)Eq, and that takes &self. In other words, one would expect it to take a reference implicitly unless forced. This is easily confirmed with a little experimentation.

It's worth noting that *self is not a move - it's a reference to a memory location. It is thus an lvalue. Ergo,

When the head expression is an lvalue, the match does not allocate a temporary location (however, a by-value binding may copy or move from the lvalue).

https://doc.rust-lang.org/stable/reference/expressions/match-expr.html

If a temporary location is not allocated, a move cannot occur. Thus the behaviour is guaranteed. As the parenthetical notes, internal data can still get moved out from destructuring pattern, which would cause a problem. This, however, is true regardless of whether you are matching on self or *self.

Using *self seems to be an informal idiom, and should be preferred.

Błażej Michalik
  • 4,474
  • 40
  • 55
Veedrac
  • 58,273
  • 15
  • 112
  • 169
  • 1
    That helps clear things up a lot. Knowing that a move cannot occur when using something as an lvalue makes a lot of sense. – KChaloux Mar 10 '16 at 18:19
3

As of Rust 1.26, the idiomatic solution is neither; you don't have to dereference the value or add & to the patterns:

fn contains_blue(&self) -> bool {
    match self {
        Color::Blue => true,
        Color::Teal => true,
        Color::Purple => true,
        _ => false,
    }
}

This is thanks to improved match ergonomics.

You can also use pattern alternation in this case:

match self {
    Color::Blue | Color::Teal | Color::Purple => true,
    _ => false,
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
1

I personally prefer the second. It conveys the intent better, in my opinion.

Steve Klabnik
  • 14,521
  • 4
  • 58
  • 99