10

I'm looking for something roughly like this take, but atomic:

impl<T: Clone> for Arc<T> {
    fn take(mut self) -> T {
        Arc::make_mut(&mut self);
        Arc::try_unwrap(self).unwrap()
    }
}

In other words, I want Arc::make_mut which returns the value itself, rather than a mutable reference.

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
Araz Abishov
  • 1,361
  • 3
  • 13
  • 24

1 Answers1

18

We can use the * deref operator to address the underlying value inside of an Rc or Arc, and then call .clone() to return a new owned clone of that value (assuming it's clonable).

use std::rc::Rc;

fn main() { 
    let rc = Rc::new("Hello".to_string());
    let mut cloned = (*rc).clone();
    cloned.truncate(4);

    // verify that it's modified
    println!("{:?}", cloned); // "Hell"
    // verify that the original was not
    println!("{:?}", rc); // "Hello"
}

The Rc/Arc semantics will prevent any mutable references from being created while your reference exists, so this operation is thread-safe; the data can't be changed while you're cloning it. You also don't need a mutable reference to the original underlying value, because you're not modifying it.

In some cases, Rust lets you omit the * deref operator: it will implicitly dereference a non-mutable pointer type if you try to call a method that doesn't exist on the pointer, but does exist on the underlying value. However, we need to be explicit in this case because a .clone() method does already exists on Rc/Arc: it's used to create a new reference to the same value. We don't want to call that, so we need to explicitly dereference to access the inner type's .clone() instead.

We can also tell Rust which .clone() method we want by explicitly calling it through the appropriate type, and the compiler will implicitly apply as many dereferences as necessary.

use std::rc::Rc;

fn main() { 
    let rc3 = Rc::new(Rc::new(Rc::new("Hello".to_string())));
    let mut cloned = String::clone(&rc3);
    cloned.truncate(4);

    // verify that it's modified
    println!("{:?}", cloned); // "Hell"
    // verify that the original was not
    println!("{:?}", rc3); // "Hello"
}
Jeremy
  • 1
  • 77
  • 324
  • 346
  • 5
    In some cases, it may be clearer to use the fully-qualified syntax. It's annoying here due to the use of the array, but in general: `::clone(&arc)` – Shepmaster Apr 18 '19 at 23:28
  • 1
    @Shepmaster Good suggestion, thanks! I changed the types and added a quick example like that to the post. (Feel free to edit if you feel inclined.) – Jeremy Apr 19 '19 at 04:25
  • Thanks a lot for a detailed answer! I don't understand why it is not possible to deref an Arc pointer into a variable and then clone it? Borrow checker screams at me that I can't move out the value out of Arc, while (*Arc).clone() is technically the same? – Araz Abishov Apr 19 '19 at 09:58
  • @ArazAbishov if your type is not `Copy`, then what would the value of the `Arc` variable be if you moved the value out of it? See also [Cannot move out of borrowed content](https://stackoverflow.com/q/28158738/155423); [Cannot move out of borrowed content in Rust](https://stackoverflow.com/q/40075031/155423); [Cannot move out of borrowed content when trying to transfer ownership](https://stackoverflow.com/q/28258548/155423); etc. – Shepmaster Apr 19 '19 at 14:45
  • 1
    I see now @Shepmaster, thanks. For some reason (probably because of experience of working with other languages), I assumed that dereferencing would be equivalent of moving an object out. That's why (*arc).clone() was confusing to me. Thanks again! – Araz Abishov Apr 19 '19 at 15:31