0

I am having a hard time getting a reference out of a RefCell<Option<T>>.

struct Consumer2<T> {
    tail: RefCell<Option<T>>,
}

impl<T> Consumer2<T> {
    fn read(&self) -> Ref<Option<&T>> {
        Ref::map(self.tail.borrow(), |v1| match v1 {
            None => &None,
            Some(v2) => &Some(v2),
        })
    }
}

The compiler point out to a lifetime issue.

For more information about this error, try `rustc --explain E0495`.
error[E0495]: cannot infer an appropriate lifetime for pattern due to conflicting requirements
  --> src/lib.rs:28:18
   |
28 |             Some(v2) => &Some(v2),
   |                  ^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 26:38...
  --> src/lib.rs:26:38
   |
26 |           Ref::map(self.tail.borrow(), |v1| match v1 {
   |  ______________________________________^
27 | |             None => &None,
28 | |             Some(v2) => &Some(v2),
29 | |         })
   | |_________^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:28:18
   |
28 |             Some(v2) => &Some(v2),
   |                  ^^
note: but, the lifetime must be valid for the anonymous lifetime defined on the method body at 25:13...
  --> src/lib.rs:25:13
   |
25 |     fn read(&self) -> Ref<Option<&T>> {
   |             ^^^^^
note: ...so that the expression is assignable
  --> src/lib.rs:26:9
   |
26 | /         Ref::map(self.tail.borrow(), |v1| match v1 {
27 | |             None => &None,
28 | |             Some(v2) => &Some(v2),
29 | |         })
   | |__________^
   = note: expected `Ref<'_, Option<&T>>`
              found `Ref<'_, Option<&T>>

I am not sure if I understand the issue itself.

Siscia
  • 1,421
  • 1
  • 12
  • 29
  • The return type `Ref – user4815162342 Jun 27 '21 at 16:01
  • Indeed, it would work. Unfortunately, my needs is a little more complex than that. But it would work. – Siscia Jun 27 '21 at 16:14
  • Can you expand your example to better represent your needs? Perhaps a better solution is possible. – user4815162342 Jun 27 '21 at 17:27
  • https://stackoverflow.com/questions/68158383/obtain-a-reference-from-a-refcelloptionrct-in-rust – Siscia Jun 28 '21 at 06:15
  • You can [edit](https://stackoverflow.com/posts/68152483/edit) this question, you don't need to ask a new one. – user4815162342 Jun 28 '21 at 06:31
  • Given the new configuration you provided, you cannot return a `Ref – user4815162342 Jun 28 '21 at 06:53
  • The question was not edited, because the use of the Rc completely changed the semantic of the question. Those are 2 completely different questions. Even if they look similar. – Siscia Jun 28 '21 at 06:55
  • @user4815162342 actually an `Option>` would be even more useful. – Siscia Jun 28 '21 at 06:56
  • The questions are 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 your actual issue. In that case it's better to edit the question to provide details than to leave the original impractical question and ask a new one, without providing context or compiler error messages, etc. – user4815162342 Jun 28 '21 at 07:03

1 Answers1

0

When you add lifetime annotations, then you get something like that:

impl<T> Consumer2<T> {
    fn read(&'s self) -> Ref<Option<&'s T>> {
        Ref::map(self.tail.borrow(), |v1: &'a Option<T>| match v1 {
            None => &None,
            Some(v2: &'a T) => &Some(v2),
        })
    }
}

The inferred lifetime for return is the lifetime of the self because of the lifetime elision rules. From Rust Book:

The second rule is if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters: fn foo<'a>(x: &'a i32) -> &'a i32.

The third rule is if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters.

So rust expects that return value will have lifetime of self.

But in closure passed to Ref::map it gets another lifetime 'a, because of the first elision rule:

(...) each parameter that is a reference gets its own lifetime parameter

Rust cannot guarantee that borrowed object will outlive the closure, so it cannot guarantee that 's and 'a lifetimes are equal or 'a outlives the 's which is required by return type inferred lifetime.

EDIT: As @Siscia pointed out this doesn't compile

In short: try to give explicit lifetime to your parameters:
impl<T> Consumer2<T> {
    fn read(&'s self) -> Ref<Option<&'s T>> {
        Ref::map(self.tail.borrow(), |v1: &'s Option<T>| match v1 {
            None => &None,
            Some(v2) => &Some(v2),
        })
    }
}

But you probably will have other problems, because returning references from closure is nearly never a good idea.

But this works:

impl<T> Consumer2<T> {
    fn read(&self) -> Ref<Option<T>> {
        Ref::map(self.tail.borrow(), |v1| match v1 {
            None => &None,
            some => &some,
        })
    }
}
Jacob99
  • 1
  • 1
  • 2
  • Does returning `&Some(v2)` ever actually compile? It looks like it returns a reference to a locally constructed option, and as such shouldn't be allowed regardless of lifetime annotations. – user4815162342 Jun 27 '21 at 16:03
  • No, the code provided does not compile as example in this answer does not compile. – Siscia Jun 27 '21 at 16:14
  • @user4815162342 I cannot check it right now, but Option is `Copy` when T: Copy, and shared references are `Copy` implicitly, so it can work. But still it's not a good idea to do it like that. – Jacob99 Jun 27 '21 at 16:19
  • Note that in your final code `some` is already `&Option`, so `some` works as well as `&some` (in fact `&some` gets auto-dereffed to `some` by the compiler). And such a `match` is equivalent to just returning `self.tail.borrow()` which, as discussed in comments to the question, is probably not what the OP wants. – user4815162342 Jun 28 '21 at 06:57