Eugene Sh.
Eugene Sh.

Reputation: 18321

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?

Upvotes: 10

Views: 5844

Answers (1)

edwardw
edwardw

Reputation: 13942

    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"),
    };        
}

Upvotes: 16

Related Questions