是否可以将 Rc<T> 映射为 Rc<Subpart-of-T>?
Is it possible to map an Rc<T> to get an Rc<Subpart-of-T>?
我有一个 Rc<Option<T>>
但需要从中得到一个 Rc<T>
。类似于:
let rc_option: Rc<Option<T>> = Rc::new(Ok(value));
let ok_value: Rc<T> = rc_option.map(|option| option.unwrap());
这甚至可能吗?这似乎应该是有意义的,因为 Rc
可以增加它在内部为新映射值设置的计数器,但我找不到它的任何文档。
简答
不,不可能从 Rc<Option<T>>
创建 Rc<T>
而使后者仍然存在。但是,可以从 Rc<Option<T>>
创建 Rc<&T>
,同时仍然保留后者变量。
长答案
如果您尝试创建一个新的 Rc<T>
,它拥有 Rc<Option<T>>
中的 T
,您将不得不消耗原始的 Rc<Option<T>>
。您也不能拥有 Rc<Option<T>>
的多个实例,因为那样的话您在指针仍然存在的情况下移动共享值,这是非常不安全的。
但是有一种方法可以安全地做到这一点!使用 Rc::try_unwrap
,您可以尝试将值移出,但如果存在多个原始 Rc
实例,这将 return 出错。
请记住,您还必须处理 Option<T>
最终变为 None
的情况。
这是一个例子:
let rc_option: Rc<Option<T>> = Rc::new(Some(value));
match Rc::try_unwrap(rc_option) {
Ok(option) => {
match option {
Some(t) => {
let ok_value: Rc<T> = Rc::new(t);
// Do something with ok_value
}
None => {
// Do something else here
}
}
}
Err(rc_option) => {
// There are multiple owners, do something else here
}
}
如果你想保留原件,你可以这样做:
match &*rc_option {
Some(ref t) => {
let ok_ref: Rc<&T> = Rc::new(t);
}
None => { /* Do something else, there's no internal value */ }
}
编辑:正如 Chronial 提到的,请注意 ok_ref
不能比 rc_option
长(因为它是对 rc_option
的引用),这可能不是您想要发生的。
正如 ThatOneDeveloper 在 中所述,标准库不支持它。您可以自己实现这样的功能:
use std::ops::Deref;
#[derive(Clone)]
struct RcSome<T>(Rc<Option<T>>);
impl<T> RcSome<T> {
fn from(rc: &Rc<Option<T>>) -> RcSome<T> {
RcSome(rc.clone())
}
}
impl<T> Deref for RcSome<T> {
type Target = T;
fn deref(&self) -> &T {
self.0.as_ref().as_ref().unwrap()
}
}
那么你可以这样做
let rc_option: Rc<Option<T>> = Rc::new(Some(value));
let ok_value: RcSome<T> = RcSome::from(&rc_option);
请注意,如果 rc_option
包含 None
,这将导致恐慌。但是 ok_value
现在的行为类似于 Rc<T>
– 也就是说,您可以 clone()
并执行 ok_value.some_method_of_T()
。 ok_value
也不与 rc_option
共享生命周期,因此它可以比它长寿。
不适用于 Rc.
这在 Rc
中是不可能的,因为它的内存布局:
// Equivalence:
struct RcBox<T> {
strong: AtomicUsize,
weak: AtomicUsize,
data: T,
};
struct Rc<T> {
ptr: *const RcBox<T>,
};
因此,计数器应该紧挨着 T
,因此您不能在两个不同的元素之间共享计数器。
可能还有其他选择。
从内存布局的角度来看,创建替代方案是完全可以接受的 FlexRc
:
struct Counters {
strong: AtomicUsize,
weak: AtomicUsize, // if support for FlexWeak is desired.
ptr: *mut (),
drop: fn(*mut ()),
}
struct FlexRc<T> {
counters: *mut Counters,
ptr: *const T,
}
理论上这个可以允许映射...但是在它上面创建一个安全接口可能并不容易。
您如何防止用户 return 在 map
中度过不相关的生命周期?保证 return 引用的生命周期超过 flex
是否足以保证安全?
fn fool(flex: FlexRc<Option<i32>>) -> FlexRc<i32> {
let i = 3;
flex.map(|_| &i)
}
我有一个 Rc<Option<T>>
但需要从中得到一个 Rc<T>
。类似于:
let rc_option: Rc<Option<T>> = Rc::new(Ok(value));
let ok_value: Rc<T> = rc_option.map(|option| option.unwrap());
这甚至可能吗?这似乎应该是有意义的,因为 Rc
可以增加它在内部为新映射值设置的计数器,但我找不到它的任何文档。
简答
不,不可能从 Rc<Option<T>>
创建 Rc<T>
而使后者仍然存在。但是,可以从 Rc<Option<T>>
创建 Rc<&T>
,同时仍然保留后者变量。
长答案
如果您尝试创建一个新的 Rc<T>
,它拥有 Rc<Option<T>>
中的 T
,您将不得不消耗原始的 Rc<Option<T>>
。您也不能拥有 Rc<Option<T>>
的多个实例,因为那样的话您在指针仍然存在的情况下移动共享值,这是非常不安全的。
但是有一种方法可以安全地做到这一点!使用 Rc::try_unwrap
,您可以尝试将值移出,但如果存在多个原始 Rc
实例,这将 return 出错。
请记住,您还必须处理 Option<T>
最终变为 None
的情况。
这是一个例子:
let rc_option: Rc<Option<T>> = Rc::new(Some(value));
match Rc::try_unwrap(rc_option) {
Ok(option) => {
match option {
Some(t) => {
let ok_value: Rc<T> = Rc::new(t);
// Do something with ok_value
}
None => {
// Do something else here
}
}
}
Err(rc_option) => {
// There are multiple owners, do something else here
}
}
如果你想保留原件,你可以这样做:
match &*rc_option {
Some(ref t) => {
let ok_ref: Rc<&T> = Rc::new(t);
}
None => { /* Do something else, there's no internal value */ }
}
编辑:正如 Chronial 提到的,请注意 ok_ref
不能比 rc_option
长(因为它是对 rc_option
的引用),这可能不是您想要发生的。
正如 ThatOneDeveloper 在
use std::ops::Deref;
#[derive(Clone)]
struct RcSome<T>(Rc<Option<T>>);
impl<T> RcSome<T> {
fn from(rc: &Rc<Option<T>>) -> RcSome<T> {
RcSome(rc.clone())
}
}
impl<T> Deref for RcSome<T> {
type Target = T;
fn deref(&self) -> &T {
self.0.as_ref().as_ref().unwrap()
}
}
那么你可以这样做
let rc_option: Rc<Option<T>> = Rc::new(Some(value));
let ok_value: RcSome<T> = RcSome::from(&rc_option);
请注意,如果 rc_option
包含 None
,这将导致恐慌。但是 ok_value
现在的行为类似于 Rc<T>
– 也就是说,您可以 clone()
并执行 ok_value.some_method_of_T()
。 ok_value
也不与 rc_option
共享生命周期,因此它可以比它长寿。
不适用于 Rc.
这在 Rc
中是不可能的,因为它的内存布局:
// Equivalence:
struct RcBox<T> {
strong: AtomicUsize,
weak: AtomicUsize,
data: T,
};
struct Rc<T> {
ptr: *const RcBox<T>,
};
因此,计数器应该紧挨着 T
,因此您不能在两个不同的元素之间共享计数器。
可能还有其他选择。
从内存布局的角度来看,创建替代方案是完全可以接受的 FlexRc
:
struct Counters {
strong: AtomicUsize,
weak: AtomicUsize, // if support for FlexWeak is desired.
ptr: *mut (),
drop: fn(*mut ()),
}
struct FlexRc<T> {
counters: *mut Counters,
ptr: *const T,
}
理论上这个可以允许映射...但是在它上面创建一个安全接口可能并不容易。
您如何防止用户 return 在 map
中度过不相关的生命周期?保证 return 引用的生命周期超过 flex
是否足以保证安全?
fn fool(flex: FlexRc<Option<i32>>) -> FlexRc<i32> {
let i = 3;
flex.map(|_| &i)
}