Match dereference

use List::*;
enum List {
    Cons(String),
    Nil,
}
impl List {
    fn new() -> List {
        Nil
    }
    fn prepend(self, elem: String) -> List {
        Cons(elem)
    }
}
fn len(list:&List) -> u32 {
    match *list {
        Cons(_) => 1,
        Nil => 0
    }
}
fn main() {
    let mut list = List::new();
    list = list.prepend(String::from("asdf"));
    println!("linked list has length: {}", len(&list));
}

match *list, I want to know, does match deconstruction gain ownership? Or will it take ownership inside this one? I can use either match *list or match list to run here, but I don't understand it.

This question also hurdles me sometimes. I think the mental model should first be this:

the scrutinee, i.e. the expression that is matched on in match expressions, and the match arms should be the same type!

So, for match *list, the type of scrutinee *list is List, so the type of arms (i.e. Cons and Nil) is List.

If you write Cons(var), i.e. bind the value to a variable, then the ownership is moved. But since List is not Copy, you can't do it from &List, and you'll get the error with solutions: Rust Playground

error[E0507]: cannot move out of `list` as enum variant `Cons` which is behind a shared reference
  --> src/main.rs:15:11
   |
15 |     match *list {
   |           ^^^^^
16 |         Cons(x) => x.len() as _,
   |              -
   |              |
   |              data moved here
   |              move occurs because `x` has type `String`, which does not implement the `Copy` trait
   |
help: consider removing the dereference here
   |
15 -     match *list {
15 +     match list {
   |

But, some ergonomics happen here, like:

  • Cons(_) contains the wildcard pattern, which doesn't consume the ownership. So your code now passes.

Unlike identifier patterns, wildcard pattern _ does not copy, move or borrow the value it matches.
src: Patterns - The Rust Reference


Update: the binding modes in the Reference book are officially ducomented as follows

When a reference value is matched by a non-reference pattern, it will be automatically treated as a ref or ref mut binding.

Non-reference patterns include all patterns except bindings, wildcard patterns (_ ), const patterns of reference types, and reference patterns.

If a binding pattern does not explicitly have ref, ref mut, or mut, then it uses the default binding mode to determine how the variable is bound. The default binding mode starts in "move" mode which uses move semantics. When matching a pattern, the compiler starts from the outside of the pattern and works inwards. Each time a reference is matched using a non-reference pattern, it will automatically dereference the value and update the default binding mode. References will set the default binding mode to ref. Mutable references will set the mode to ref mut unless the mode is already ref in which case it remains ref. If the automatically dereferenced value is still a reference, it is dereferenced and this process repeats.

Move bindings and reference bindings can be mixed together in the same pattern. Doing so will result in partial move of the object bound to and the object cannot be used afterwards. This applies only if the type cannot be copied.

src: Patterns - The Rust Reference

1 Like

The short answer is that a match itself doesn't take ownership, or borrows, or really does anything. It's the pattern you are matching against that can take ownership or borrow, all or part of your value.

A _ pattern does neither, and a enum variant constructor pattern just reads the variant (which acts like a very short read/borrow of the variant "field"/information of the enum).

A variable pattern will take ownership, and thus move (or copy if possible). The exception to this rule is when "match ergonomics" come into play, which give variable patterns a different meaning inside of struct or variant (or tuple, slice, etc..) patterns that "incorrectly" matched against something that actually was a reference.

The explicit way to five a variable pattern a non-ownership-taking behavior is by annotation the binding mode with syntax sich as ref x or ref mut x.


Fun fact, a match by itself (with just a _ pattern) does so little to the thing being matched on, it doesn't even care whether the variable you match on is currently mutably borrowed.

3 Likes

Woo, that's exactly what

wildcard pattern _ does not ... borrow the value it matches

means :laughing:

1 Like