Reputation: 15829
I have an Rc<Option<T>>
but need to get an Rc<T>
from it. Something like:
let rc_option: Rc<Option<T>> = Rc::new(Ok(value));
let ok_value: Rc<T> = rc_option.map(|option| option.unwrap());
Is this even remotely possible? It seems like something that should make sense, as the Rc
could just increment the counter it has internally for the new mapped value, but I can't find any docs for it.
Upvotes: 4
Views: 255
Reputation: 299790
This is not possible with Rc
simply because of its memory layout:
// Equivalence:
struct RcBox<T> {
strong: AtomicUsize,
weak: AtomicUsize,
data: T,
};
struct Rc<T> {
ptr: *const RcBox<T>,
};
Therefore, the counters are expected to be right next to T
, and so you cannot share counters between two distinct elements.
From a memory-layout point of view, it is perfectly acceptable to create an alternative 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,
}
And this one could in theory allow mapping... however creating a safe interface over it may not be easy.
How do you prevent the user from returning an unrelated lifetime in map
? Is guaranteeing the lifetime of the return reference exceeds that of flex
sufficient to be safe?
fn fool(flex: FlexRc<Option<i32>>) -> FlexRc<i32> {
let i = 3;
flex.map(|_| &i)
}
Upvotes: 2
Reputation: 70663
As ThatOneDeveloper stated in their answer, that is not supported by the standard library. You could implement such a feature yourself:
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()
}
}
Then you can do this
let rc_option: Rc<Option<T>> = Rc::new(Some(value));
let ok_value: RcSome<T> = RcSome::from(&rc_option);
Note that this will panic if rc_option
contains None
. But ok_value
will now behave like a Rc<T>
– i.e. you can clone()
it and do ok_value.some_method_of_T()
. ok_value
does also not share a lifetime with rc_option
, so it can outlive it.
Upvotes: 1
Reputation: 466
No, it's not possible to create a Rc<T>
from an Rc<Option<T>>
that leaves the latter still existing. It is possible to create an Rc<&T>
however, from a Rc<Option<T>>
, while still leaving the latter variable existing.
If you're trying to create a new Rc<T>
that owns the T
inside the Rc<Option<T>>
, you will have to consume the original Rc<Option<T>>
. You also can't have multiple instances of the Rc<Option<T>>
, because then you're moving the shared value while pointers still exist, which is very unsafe.
But there is a way to do this safely! Using Rc::try_unwrap
, you can attempt to move the value out, but this will return an error if multiple instances of the original Rc
exist.
Keep in mind you also have to handle the scenario where Option<T>
ends up being None
.
Here's an example of this:
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
}
}
If you wanted to preserve the original, you could do this:
match &*rc_option {
Some(ref t) => {
let ok_ref: Rc<&T> = Rc::new(t);
}
None => { /* Do something else, there's no internal value */ }
}
EDIT: As Chronial mentioned, do note that the ok_ref
cannot outlive rc_option
(because it's a reference to rc_option
), which may not be what you want to happen.
Upvotes: 7