Moving value outside Rc<T> where T does not implement Copy Trait

Hello everyone,

I would like to create a library that provides some access to another library. In other word, I create a Holder struct that contains a V struct on which I can apply some changes. I tried to keep the example below as simple as possible.

Since the V struct is external to my crate, I assume I cannot do any change to its code. In practice, V is a complex struct and cloning it comes at a large cost.

For the Holder struct, methods can only take references to Holder, not move objects. This restriction is very important.

Do you know if it is still possible to call V.add without actually moving other: &Holder inside the Holder.add method?

Thanks!

use std::rc::Rc;

/// Some example struct I cannot directly edit
/// And that does not implement the Copy trait
#[derive(Debug)]
struct V {
    value: i32,    
}

impl V {
    fn add(&mut self, other: Self) {
        self.value += other.value;
    }
}

/// Some struct I write
/// to store an inner struct V
#[derive(Debug)]
struct Holder {
    value: Rc<V>,
}


impl Holder {
    /// Function that calls V.add
    /// Here, I can only references to Holder
    /// either &Self or &mut Self
    fn add(&mut self, other: &mut Self) {
        // How to call V.add without cloning
        // Holder.value, since it can cost a lot of time?
        let other: V = Rc::try_unwrap(other.value).unwrap();
        self.value.add(other); // This does not work, obviously
    }
}

fn main() {
    let mut v1 = V { value: 1 };
    let mut v2 = V { value: 2 };
    
    let mut h1 = Holder { value: Rc::new(v1) };
    let mut h2 = Holder { value: Rc::new(v2) };
    
    h1.add(&mut h2);
    
    println!("h1: {:?}", h1);
    println!("h2: {:?}", h2);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of `other.value` which is behind a mutable reference
  --> src/main.rs:30:39
   |
30 |         let other: V = Rc::try_unwrap(other.value).unwrap();
   |                                       ^^^^^^^^^^^ move occurs because `other.value` has type `Rc<V>`, which does not implement the `Copy` trait

error[E0596]: cannot borrow data in an `Rc` as mutable
  --> src/main.rs:31:9
   |
31 |         self.value.add(other); // This does not work, obviously
   |         ^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<V>`

warning: variable does not need to be mutable
  --> src/main.rs:36:9
   |
36 |     let mut v1 = V { value: 1 };
   |         ----^^
   |         |
   |         help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

warning: variable does not need to be mutable
  --> src/main.rs:37:9
   |
37 |     let mut v2 = V { value: 2 };
   |         ----^^
   |         |
   |         help: remove this `mut`

Some errors have detailed explanations: E0507, E0596.
For more information about an error, try `rustc --explain E0507`.
warning: `playground` (bin "playground") generated 2 warnings
error: could not compile `playground` due to 2 previous errors; 2 warnings emitted

Well, cloning other.value itself wouldn't be expensive, since that's only cloning the Rc, not the inner value. However, if you have a clone of an Rc, then that's by construction not a unique reference, so try_unwrap() will always fail. So this can't be done – you can't just move out of an Rc, similarly to how you aren't allowed to move out of any other kind of (non-uniquely-owning) pointer, either.

As for the other problem: if you are sure that the Holder is the unique owner of the inner value, then you can write Rc::get_mut(&mut self.value).unwrap().add(…), but that doesn't work in general.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.