Self dereference matching

Hi, very beginner here. I'm trying a linked list implementation with enums. I've written an nth method to look up values by index:

use crate::List::*;

enum List {
    Cons(i32, Box<List>),
    Nil
}

impl List {
    fn new() -> List {
        Nil
    }

    fn add(self, elem: i32) -> List {
        Cons(elem, Box::new(self))
    }

    fn nth(&self, index: u32) -> i32 {
        match (index, self) {
            (0, Cons(elem, _))  => *elem,
            (_, Cons(_, list))  => (*list).nth(index - 1),
            (_, Nil)            => -999 // Throw an error
        }
    }
}

This works fine, but a slightly different implementation of nth does not:

fn nth(&self, index: u32) -> i32 {
    match (index, *self) {
        (0, Cons(elem, _))  => elem,
        (_, Cons(_, list))  => (*list).nth(index - 1),
        (_, Nil)            => -999 // Throw an error
    }
}

Here, the *self is problematic and raises this error.

error[E0507]: cannot move out of `*self` which is behind a shared reference
  --> r-03-03.rs:39:23
   |
39 |         match (index, *self) {
   |                       ^^^^^ move occurs because `*self` has type `List`, which does not 
implement the `Copy` trait

I cannot understand these messages, especially since a method which employs match *self in a similar way does not raise this error.

fn head(&self) -> i32 {
    match *self {
        Cons(elem, _)       => elem,
        Nil                 => -999 // Throw an error
    }
}

The above works fine.

Basically, why does match (index, *self) fail where match *self is acceptable?

The reason that your second snippet fails to compile is that to perform that match, it must first create a tuple (pair) which is going to have the type (u32, List), but the type of this tuple indicates that the tuple has ownership of the list. Since you don't have ownership of the List, but just a borrow, you cannot give away ownership to the tuple you are creating, hence the error.

The reason that the first snippet compiles is that the tuple you are creating there has the type (u32, &List), so you don't need ownership to create the tuple.

The reason that the third one compiles is that the match is not actually taking ownership of self. Checking whether self is Cons or Nil and extracting the value of elem doesn't require taking any ownership as elem is Copy. On the other hand, it will fail to compile if you try to extract the value of list too, since that field is not Copy.

fn nth(&self, index: u32) -> i32 {
    match *self {
        Cons(elem, list) => elem,
        Nil => -999
    }
}
error[E0507]: cannot move out of `self.1` which is behind a shared reference
  --> src/lib.rs:18:11
   |
18 |     match *self {
   |           ^^^^^ help: consider borrowing here: `&*self`
19 |         Cons(elem, list) => elem,
   |                    ----
   |                    |
   |                    data moved here
   |                    move occurs because `list` has type `Box<List>`, which does not implement the `Copy` trait

But yeah, in short, it failed because matching on a tuple would require creating a temporary value, and this temporary value would take ownership of self.

3 Likes