1

I am having problem getting a reference out of a RefCell<Option<Rc>>.

Any suggestion?

struct Node<T> {
    value: T
}

struct Consumer3<T> {
    tail: RefCell<Option<Rc<Node<T>>>>,
}

impl<T> Consumer3<T> {
    fn read<'s>(&'s self) -> Ref<Option<T>> {
        Ref::map(self.tail.borrow(), |f| {
            f.map(|s| {
                let v = s.as_ref();
                v.value
            })
        })
    }
}

Gives:

error[E0308]: mismatched types
  --> src/lib.rs:15:13
   |
15 | /             f.map(|s| {
16 | |                 let v = s.as_ref();
17 | |                 v.value
18 | |             })
   | |______________^ expected reference, found enum `Option`
   |
   = note: expected reference `&_`
                   found enum `Option<T>`
help: consider borrowing here
   |
15 |             &f.map(|s| {
16 |                 let v = s.as_ref();
17 |                 v.value
18 |             })
   |

error: aborting due to previous error

Playground

Jmb
  • 18,893
  • 2
  • 28
  • 55
Siscia
  • 1,421
  • 1
  • 12
  • 29
  • @Jmb please re-open the question, the use of the Rc completely change the semantinc of what we are trying to achieve. From https://stackoverflow.com/questions/68152483/obtain-the-a-reference-from-a-refcelloptiont-in-rust – Siscia Jun 28 '21 at 06:53
  • I've voted to reopen the question because I suspect the answers are different. The duplicate answer says "yes you can do that, here's how" but I believe the answer to *this* question is either "you cannot" or maybe "you can but with a different mechanism" – kmdreko Jun 28 '21 at 07:02
  • @kmdreko The questions are indeeed different, but the original question doesn't make much sense as asked because it would be trivially answered by returning `self.tail.borrow()`, which obviously doesn't resolve the OP's issue. In that case I believe it's much better to edit the question to provide details than to leave the original impractical question and ask a new one, which the OP did without providing any context, compiler error messages, etc. – user4815162342 Jun 28 '21 at 07:04
  • If the original question is left unedited, then this question is likely a duplicate of [this one](https://stackoverflow.com/q/67243000/1600898). – user4815162342 Jun 28 '21 at 07:05
  • An `Rc` inside a `RefCell` is different from an `RefCell` inside an `Rc`. – Siscia Jun 28 '21 at 10:15
  • Returning `Ref – eggyal Jun 28 '21 at 11:09
  • Hence, it will be impossible also to return also an `Option>` ? – Siscia Jun 28 '21 at 11:14
  • No, [that's possible](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=46318ed01ad1d4406268de593cc48315). – eggyal Jun 28 '21 at 11:27

1 Answers1

5

Mapping from one Ref to another requires that the target already exist in memory somewhere. So you can't get a Ref<Option<T>> from a RefCell<Option<Rc<Node<T>>>>, because there's no Option<T> anywhere in memory.

However, if the Option is Some, then there will be a T in memory from which you can obtain a Ref<T>; if the Option is None, obviously you can't. So returning Option<Ref<T>> may be a viable alternative for you:

use std::{cell::{Ref, RefCell}, rc::Rc};

struct Node<T> {
    value: T
}

struct Consumer3<T> {
    tail: RefCell<Option<Rc<Node<T>>>>,
}

impl<T> Consumer3<T> {
    fn read(&self) -> Option<Ref<T>> {
        let tail = self.tail.borrow();
        
        if tail.is_some() {
            Some(Ref::map(tail, |tail| {
                let node = tail.as_deref().unwrap();
                &node.value
            }))
        } else {
            None
        }
    }
}

Playground.

eggyal
  • 122,705
  • 18
  • 212
  • 237
  • Thanks a lot! This code was extremelly tricky! I appreciate your help! – Siscia Jun 28 '21 at 18:18
  • And, for those coming here with Rust >= 1.63.0, I think this can be done with [`Ref::filter_map`](https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map). [Playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b2832c909efddb761566d34079584656) – Mark Lodato Jun 20 '23 at 16:09