Rc<Trait> 到 Option<T>?

Rc<Trait> to Option<T>?

我正在尝试实现如下所示的方法:

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() 似乎要求 Rc 中包含的类型为 Sized,理想情况下我不想使用 unsafe块。

有什么办法可以实现吗?

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

如果您希望 concretify 函数将原始值移出 Rc,我认为它不可能实现;请参阅 this question 了解原因。

如果您愿意 return 克隆,那很简单:

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

这输出:

Got back: Some(Foo(42))

Got back: None

Playground

如果你想要更“动人”的东西,并且创造你的价值很便宜,你也可以做一个假人,使用 downcast_mut() 而不是 downcast_ref(),然后 std::mem::swap 与假人。

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

唯一可以 return Rc 内部项的方法是 Rc::try_unwrap,但是 returns Result<T, Rc<T>> 需要 TSized.

为了做你想做的事,你需要一个带有签名的方法:Rc<T> -> Result<Box<T>, Rc<T>>,这将允许 T 成为 !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>,
}

因此,您唯一可以从 Rc<T> 中脱身的 BoxBox<RcBox<T>>

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

  • single-allocation 要求所有 3 个元素都在一个 struct
  • T: ?Sized 要求 T 是最后一个字段

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


但是,在您的具体情况下,绝对有可能改进一般情况。当然,它确实需要 unsafe 代码。虽然它与 Rc 一起工作得很好,但用 Arc 实施它会因潜在的 data-races.

而变得复杂

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

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