Search code examples
rustpattern-matchingownership

Matching with Rc<RefCell<T>>


Consider the following code - which compiles and works:

use std::rc::Rc;
use std::cell::RefCell;
use crate::Foo::{Something, Nothing};

enum Foo {
    Nothing,
    Something(i32),
}

fn main() {
    let wrapped = Rc::new(RefCell::new(Foo::Nothing));
    //....

    match *wrapped.borrow() {
        Something(x) => println!("{}", x),
        Nothing => println!("Nothing"),
    };        
}

Now I want to match two wrapped values instead of just one:

use std::rc::Rc;
use std::cell::RefCell;
use crate::Foo::{Something, Nothing};

enum Foo {
    Nothing,
    Something(i32),
}

fn main() {
    let wrapped1 = Rc::new(RefCell::new(Foo::Nothing));
    let wrapped2 = Rc::new(RefCell::new(Foo::Nothing));
    //....

    match (*wrapped1.borrow(), *wrapped2.borrow()) {
        (Something(x), Something(y)) => println!("{}, {}", x, y),
        _ => println!("Nothing"),
    };        
}

Now this will give the compile error:

error[E0507]: cannot move out of dereference of `std::cell::Ref<'_, Foo>`
  --> src\main.rs:16:12
   |
16 |     match (*wrapped1.borrow(), *wrapped2.borrow()) {
   |            ^^^^^^^^^^^^^^^^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait

error[E0507]: cannot move out of dereference of `std::cell::Ref<'_, Foo>`
  --> src\main.rs:16:32
   |
16 |     match (*wrapped1.borrow(), *wrapped2.borrow()) {
   |                                ^^^^^^^^^^^^^^^^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait

I do not quite understand the fundamental difference between the semantics of the two examples. Why is it happening and what would be the correct way of making the second snippet work?


Solution

  •     match (*wrapped1.borrow(), *wrapped2.borrow()) {
    

    You created a tuple here on the spot. And the values were being moved into the newly created tuple. This works though:

    use std::rc::Rc;
    use std::cell::RefCell;
    use crate::Foo::Something;
    
    enum Foo {
        Nothing,
        Something(i32),
    }
    
    fn main() {
        let wrapped1 = Rc::new(RefCell::new(Foo::Nothing));
        let wrapped2 = Rc::new(RefCell::new(Foo::Nothing));
    
        match (&*wrapped1.borrow(), &*wrapped2.borrow()) {
            (Something(x), Something(y)) => println!("{}, {}", x, y),
            _ => println!("Nothing"),
        };        
    }