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
如果你想要更“动人”的东西,并且创造你的价值很便宜,你也可以做一个假人,使用 downcast_mut()
而不是 downcast_ref()
,然后 std::mem::swap
与假人。
不幸的是,Rc
的 API 似乎缺少在 !Sized
时获得包装类型所有权的必要方法。
唯一可以 return Rc
内部项的方法是 Rc::try_unwrap
,但是 returns Result<T, Rc<T>>
需要 T
是 Sized
.
为了做你想做的事,你需要一个带有签名的方法: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>
中脱身的 Box
是 Box<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));
}
我正在尝试实现如下所示的方法:
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
如果你想要更“动人”的东西,并且创造你的价值很便宜,你也可以做一个假人,使用 downcast_mut()
而不是 downcast_ref()
,然后 std::mem::swap
与假人。
不幸的是,Rc
的 API 似乎缺少在 !Sized
时获得包装类型所有权的必要方法。
唯一可以 return Rc
内部项的方法是 Rc::try_unwrap
,但是 returns Result<T, Rc<T>>
需要 T
是 Sized
.
为了做你想做的事,你需要一个带有签名的方法: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>
中脱身的 Box
是 Box<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));
}