Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to map an Rc<T> to get an Rc<Subpart-of-T>?

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.

like image 615
Callum Rogers Avatar asked Nov 07 '25 00:11

Callum Rogers


2 Answers

Short Answer

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.

Long Answer

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.

like image 116
ThatOneDeveloper Avatar answered Nov 08 '25 13:11

ThatOneDeveloper


Not with Rc.

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.


Alternatives are likely possible.

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)
}
like image 23
Matthieu M. Avatar answered Nov 08 '25 15:11

Matthieu M.