Rc 或 Arc 是否有克隆底层值并将其返回给调用者的操作?

问题描述 投票:0回答:3

我正在寻找大致类似这样的东西

take
,但是原子的:

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

换句话说,我想要

Arc::make_mut
返回值本身,而不是可变引用。

rust smart-pointers reference-counting
3个回答
36
投票

我们可以使用

*
deref 运算符来寻址
Rc
Arc
内部的基础值,然后调用
.clone()
返回该值的新拥有的克隆(假设它是可克隆的)。

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"
}

Rc
/
Arc
语义将防止在引用存在时创建任何可变引用,因此此操作是线程安全的;克隆数据时无法更改数据。您也不需要对原始基础值的可变引用,因为您没有修改它。

在某些情况下,Rust 允许您省略

*
deref 运算符:如果您尝试调用指针上不存在但底层值上确实存在的方法,它将隐式取消引用非可变指针类型。然而,在这种情况下我们需要明确,因为
.clone()
方法确实已经存在于
Rc
/
Arc
上:它用于创建对相同值的新引用。我们不想调用它,因此我们需要显式取消引用来访问内部类型的
.clone()

我们还可以通过适当的类型显式调用它来告诉 Rust 我们想要哪个

.clone()
方法,编译器将根据需要隐式应用尽可能多的取消引用。

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"
}

1
投票

接受的答案可能会不必要地克隆数据,即使没有其他强有力的引用。

但是您可以执行以下操作:

pub fn take<T: Clone>(v: Arc<T>) -> T {
    match Arc::try_unwrap(v) {
        Ok(v) => v,
        Err(v) => T::clone(&v),
    }
}

如果只有一个强引用,这将避免克隆,

make_mut()
也是如此。

请注意,对于

Rc
,原始帖子中的代码就足够了,因为它不需要是原子的。


0
投票

现在每晚都有

Arc::unwrap_or_clone()
Rc::unwrap_or_clone()
,正是针对此用例。

© www.soinside.com 2019 - 2024. All rights reserved.