不幸的是,它的 API 似乎Rc
缺少必要的方法来获得包装类型的所有权!Sized
。
唯一可以返回 a 的内部项的方法Rc
是Rc::try_unwrap
,但是它返回Result<T, Rc<T>>
的方法要求T
是Sized
。
为了做你想做的事,你需要一个带有签名的方法: Rc<T> -> Result<Box<T>, Rc<T>>
,这将允许T
be !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: ?Sized
T
作为最后一个字段的任务
所以总体来说提升空间不大。
但是,在您的具体情况下,绝对有可能改善一般情况。当然,它确实需要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));
}