3
use crate::List::{Cons, Nil};

#[derive(Debug)]
struct Foo {}

#[derive(Debug)]
enum List {
    Cons(i32, Foo),
    Nil,
}

impl List {
    fn tail(&self) -> Option<&Foo> {
        match self {
            Cons(_, item) => Some(item), // why `item` is of type `&Foo`?
            Nil => None,
        }
    }
}

As stated in the comment, why is item of type &Foo? What is the rule that says item will be type &Foo rather than Foo?

I understand that it does not make sense for item to be Foo; &self says self is a reference, so it does not make sense to move a value out of a reference, but are there any specifications that define the rules clearly?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Helin Wang
  • 4,002
  • 1
  • 30
  • 34
  • @SergioTulentsev thanks for your reply. Actually RefCell is unrelated to what I am trying to ask. Removed RefCell to avoid confusion. – Helin Wang Jun 09 '19 at 01:41

1 Answers1

8

RFC 2005 (a.k.a. match ergonomics) introduced the rule.

Before the change was implemented, there were 2 ways to write this match.

  1. Match on self and prefix each pattern with & to "destructure" the reference.

    fn tail(&self) -> Option<&Foo> {
        match self {
            &Cons(_, ref item) => Some(item),
            &Nil => None,
        }
    }
    
  2. Match on *self and don't prefix each pattern with & (because *self is not a reference).

    fn tail(&self) -> Option<&Foo> {
        match *self {
            Cons(_, ref item) => Some(item),
            Nil => None,
        }
    }
    

Yet, in both cases, we need to write ref item, otherwise we'll get error[E0507]: cannot move out of borrowed content.

However, in the match you've written, the expression being matched is a reference (type &List) but the patterns are not reference patterns (as in 1. above). This is where match ergonomics kick in: the rule says that when a reference is matched with a non-reference pattern, the bindings within that pattern bind by reference rather than by value (i.e. as if they were prefixed with ref).

Francis Gagné
  • 60,274
  • 7
  • 180
  • 155