Rust cannot move out of enum variant

In my efforts to learn Rusts’ notorious borrow checker I’m hopelessly stuck. I’ve constructed a tree structure that loosely represents a file system. As soon as I’m trying to borrow a enum value and try to mutate the underlying value, I get an error that I don’t understand. Pretend that cache_files actually has some elements in it. Here’s the code:

enum UnixFile{
    FILE(UFile),
    FOLDER(UFolder)
}

struct UFile {
    name: String,
    parent: Weak<RefCell<UnixFile>>,
    size: usize,
}

struct UFolder {
    name: String,
    parent: Weak<RefCell<UnixFile>>,
    files: Vec<Rc<RefCell<UnixFile>>>,
}

fn main() {
    let root = Rc::new(RefCell::new(
        UnixFile::FOLDER(
            UFolder {
                name: String::from("root"),
                parent: Weak::new(),
                files: vec![],
            })));

    let mut current = root.clone();

    let mut f = &*(*current).borrow_mut();

    let mut cache_files:Vec<Rc<RefCell<UnixFile>>> = Vec::new();

    match f {
        UnixFile::FOLDER(mut folder) => {
            folder.files.append(&mut cache_files);
        },
        UnixFile::FILE(file) => {}
    }
}

And this is the resulting error:

error[E0507]: cannot move out of `f` as enum variant `FOLDER` which is behind a shared reference
  --> src/main.rs:43:11
   |
43 |     match f {
   |           ^
44 |         UnixFile::FOLDER(mut folder) => {
   |                          ----------
   |                          |
   |                          data moved here
   |                          move occurs because `folder` has type `UFolder`, which does not implement the `Copy` trait

So what does this error mean? And how can I put the code in a position that I actually can add a file from cache_files to the folder?

>Solution :

First, you do &* for f. This gives you a shared reference. You cannot mutate it.

Instead, do &mut *. You can also simplify the expression further, removing the parentheses and asterisk thanks to auto-deref:

let mut f = &mut *current.borrow_mut();

The second problem is more subtle. When you specify UnixFolder::FOLDER(mut folder) in the match arm, the mut forces the compiler to move out of folder instead of just binding it to a reference. The explanation of why is complicated and not really relevant (what happens is that it opts out for match ergonomics, but you don’t need to understand that). What you need to know is that you can just remove the mut, and then folder will have type &mut UFolder, and everything will go fine.

Leave a Reply