4

My goal is to delegate method calls against my struct to a Trait's methods, where the Trait object is inside an Rc of RefCell.

I tried to follow the advice from this question: How can I obtain an &A reference from a Rc<RefCell<A>>?

I get a compile error.

use std::rc::Rc;
use std::cell::RefCell;
use std::fmt::*;
use std::ops::Deref;

pub struct ShyObject {
    pub association: Rc<RefCell<dyn Display>>
}

impl Deref for ShyObject {
    type Target = dyn Display;
    fn deref<'a>(&'a self) -> &(dyn Display + 'static) {
        &*self.association.borrow()
    }
}

fn main() {}

Here is the error:

error[E0515]: cannot return value referencing temporary value
  --> src/main.rs:13:9
   |
13 |         &*self.association.borrow()
   |         ^^-------------------------
   |         | |
   |         | temporary value created here
   |         returns a value referencing data owned by the current function

My example uses Display as the trait; in reality I have a Trait with a dozen methods. I am trying to avoid the boilerplate of having to implement all those methods and just burrow down to the Trait object in each call.

Boiethios
  • 38,438
  • 19
  • 134
  • 183
Paul Chernoch
  • 5,275
  • 3
  • 52
  • 73
  • 2
    Related, but perhaps not quite duplicates: [How do I return a reference to something inside a RefCell without breaking encapsulation?](https://stackoverflow.com/questions/29401626/how-do-i-return-a-reference-to-something-inside-a-refcell-without-breaking-encap) and [How to borrow the T from a RefCell as a reference?](https://stackoverflow.com/questions/51349577/how-to-borrow-the-t-from-a-refcellt-as-a-reference). Note that both of these are solved by a method that *returns a smart pointer* implementing `Deref`, not by simply implementing `Deref` for the main type (`ShyObject` in your case). – trent Sep 09 '19 at 14:55

2 Answers2

4

You cannot do that. borrow creates a new struct that allows RefCell to track the borrow. You're then not allowed to return a borrow to this Ref, because it is a local variable.

Boiethios
  • 38,438
  • 19
  • 134
  • 183
  • Is there an alternate way to use Deref to facilitate delegation? Some magic with lifetimes? – Paul Chernoch Sep 09 '19 at 14:48
  • 2
    @PaulChernoch Lifetime annotations don't change how long things live - they just describe to the compiler what lifetime constraints you want. If you want impossible constraints then no annotations will help. – Peter Hall Sep 09 '19 at 14:51
  • 1
    @PaulChernoch Nope, by definition you cannot. – Boiethios Sep 09 '19 at 14:51
4

You can't. RefCell::borrow returns a Ref<T>, not a &T. If you try to do this in a method then you will need to first borrow the Ref<T> but it will go out of scope.

Instead of implementing Deref, you could have a method that returns something that does:

impl ShyObject {
    fn as_deref(&self) -> impl Deref<Target = dyn Display> {
        self.association.borrow()
    }
}

Otherwise, since you only want to expose the Display implementation of the inner data anyway, you can workaround it by actually dereferencing a different type which delegates:

pub struct ShyObject {
    association: Assocation<dyn Display>,
}

struct Assocation<T: ?Sized>(Rc<RefCell<T>>);

impl<T: Display + ?Sized> fmt::Display for Assocation<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.borrow())
    }
}

impl Deref for ShyObject {
    type Target = dyn Display + 'static;
    fn deref(&self) -> &Self::Target {
        &self.association
    }
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204