2

我正在尝试实现一个看起来像这样的方法:

fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
    Rc::try_unwrap(rc).ok().and_then(|trait_object| {
        let b: Box<Any> = unimplemented!();
        b.downcast().ok().map(|b| *b)
    })
}

但是,try_unwrap不适用于特征对象(这是有道理的,因为它们没有大小)。我的下一个想法是尝试找到一些Rc<Any>直接展开的函数Box<Any>。我能找到的最接近的东西是

if Rc::strong_count(&rc) == 1 {
    Some(unsafe {
        Box::from_raw(Rc::into_raw(rc))
    })
} else {
    None
}

但是,Rc::into_raw()似乎要求 to 中包含的类型RcSized,理想情况下我不想使用unsafe块。

有什么方法可以实现吗?

Playground Link,我正在寻找rc_to_box这里的实现。

4

2 回答 2

3

不幸的是,它的 API 似乎Rc缺少必要的方法来获得包装类型的所有权!Sized

唯一可以返回 a 的内部项的方法RcRc::try_unwrap,但是它返回Result<T, Rc<T>>的方法要求TSized

为了做你想做的事,你需要一个带有签名的方法: Rc<T> -> Result<Box<T>, Rc<T>>,这将允许Tbe !Sized,然后你可以从那里提取Box<Any>并执行downcast调用。

但是,由于实施方式的原因,这种方法是不可能Rc的。这是一个精简的版本Rc

struct RcBox<T: ?Sized> {
    strong: Cell<usize>,
    weak: Cell<usize>,
    value: T,
}

pub struct Rc<T: ?Sized> {
    ptr: *mut RcBox<T>,
    _marker: PhantomData<T>,
}

因此,您唯一Box可以摆脱的Rc<T>Box<RcBox<T>>

请注意,此处的设计受到严格限制:

  • 单一分配要求所有 3 个元素都在一个单一的struct
  • T: ?SizedT作为最后一个字段的任务

所以总体来说提升空间不大。


但是,在您的具体情况下,绝对有可能改善一般情况。当然,它确实需要unsafe代码。虽然它与 . 一起工作得相当好,但由于潜在的数据竞争Rc,实现它Arc会变得复杂。

哦...代码按原样提供,不暗示任何保证;)

use std::any::Any;
use std::{cell, mem, ptr};
use std::rc::Rc;

struct RcBox<T: ?Sized> {
    strong: cell::Cell<usize>,
    _weak: cell::Cell<usize>,
    value: T,
}

fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
    //  Will be responsible for freeing the memory if there is no other weak
    //  pointer by the end of this function.
    let _guard = Rc::downgrade(&rc);

    unsafe {
        let killer: &RcBox<Any> = {
            let killer: *const RcBox<Any> = mem::transmute(rc);
            &*killer 
        };

        if killer.strong.get() != 1 { return None; }

        //  Do not forget to decrement the count if we do take ownership,
        //  as otherwise memory will not get released.
        let result = killer.value.downcast_ref().map(|r| {
            killer.strong.set(0);
            ptr::read(r as *const T)
        });

        //  Do not forget to destroy the content of the box if we did not
        //  take ownership
        if result.is_none() {
            let _: Rc<Any> = mem::transmute(killer as *const RcBox<Any>);
        }

        result
    }
}

fn main() {
    let x: Rc<Any> = Rc::new(1);
    println!("{:?}", concretify::<i32>(x));
}
于 2017-01-12T19:07:10.050 回答
2

concretify如果您期望它将原始值移出Rc;我认为不可能实现您的功能 看到这个问题为什么。

如果您愿意返回一个克隆,这很简单:

fn concretify<T: Any+Clone>(rc: Rc<Any>) -> Option<T> {
    rc.downcast_ref().map(Clone::clone)
}

这是一个测试:

#[derive(Debug,Clone)]
struct Foo(u32);

#[derive(Debug,Clone)]
struct Bar(i32);

fn main() {
    let rc_foo: Rc<Any> = Rc::new(Foo(42));
    let rc_bar: Rc<Any> = Rc::new(Bar(7));
    
    let foo: Option<Foo> = concretify(rc_foo);
    println!("Got back: {:?}", foo);
    let bar: Option<Foo> = concretify(rc_bar);
    println!("Got back: {:?}", bar);
}

这输出:

回来了:一些(Foo(42))

回来了:无

操场

如果你想要一些更“动感”的东西,并且创造你的价值观很便宜,你也可以制作一个假人,使用downcast_mut()代替downcast_ref(),然后std::mem::swap使用假人。

于 2017-01-12T17:34:23.483 回答