The trait .. can not be made into an object

pub trait Foo_T {
  fn foo(self:  Rc<Self>);
}

pub trait Bar_T {
  fn bar(self: &Rc<Self>); // note the extra `&`
}

Rc<dyn Foo_T>; // okay
Rc<dyn Bar_T>; // not okay

I have no idea what is happening here. Can anyone enlighten me on wtf is going on ?
How is Rc<Self> -> &Rc<Self> causing this problem ?

So, if you have a Rc<dyn Foo_T> and call the foo method, what happens is that the Rc gets converted into the appropriate Rc<UnderlyingStruct> and then passed to UnderlyingStruct::foo.

However, you can't convert &Rc<dyn Bar_T> into a &Rc<UnderlyingStruct> because the conversion modifies the Rc, which you only have immutable access to.

6 Likes

More generally, see below and link chase the "go backwards" part (sorry for the brevity; on mobile).

2 Likes

See the unstable DispatchFromDyn trait for more info:

Imagine we have a trait object t with type &dyn Tr, where Tr is some trait with a method m defined as fn m(&self);. When calling t.m(), the receiver t is a wide pointer, but an implementation of m will expect a narrow pointer as &self (a reference to the concrete type). The compiler must generate an implicit conversion from the trait object/wide pointer to the concrete reference/narrow pointer.

It works this way because Rc<FooStruct> implements DispatchFromDyn<Rc<dyn Foo_T>>, but &Rc<BarStruct> does not implement DispatchFromDyn<&Rc<dyn Bar_T>>.

2 Likes

If DispatchFromDyn was available for custom types (as well as more/custom types being allowed for self types to begin with), then something like rc_borrow::RcBorrow<'_, T> (crate by @CAD97) could implement it, which is a type that aims to work analogously to &Rc<T>, but without double indirection.

Anyways… once you work with dyn Trait already, you can probably also afford the overhead of unconditionally cloning an Rc (assuming the idea behind using &Rc<Self> was that you only conditionally need a clone of the Rc), so I guess all this isn’t too limiting, after all.

4 Likes

From the other posts it is obviously a little complex to us &Rc, but why do you need to borrow an Rc? Did you just do it by accident and wonder why it didn't work, or do you have a need for it?

1 Like

Some library has a func

foo(self: Rc<Self>) ...

I have a function that may or may not call foo

fn my_fn(self: &Rc<Self>) {
  if (blah) {
    self.clone().foo();
  } else {
  }
}

then I want to put my_fn in a trait.

I mean, it it really, really must be avoided, something like this could work

use rc_borrow::RcBorrow;
use std::rc::Rc;

// Not object safe, but `DynBar` is, so use `dyn DynBar`!
pub trait Bar: DynBar {
    fn bar(self: &Rc<Self>);
    // Non-method, `RcBorrow`-accepting version
    fn bar_rc_borrow(this: RcBorrow<'_, Self>);
}

/// Implement this trait to implement `DynBar` and thus `Bar`
pub trait BarImpl {
    /// Implement this to provide `Bar::bar`.
    fn bar(this: RcBorrow<'_, Self>);
}

/// # Safety
/// (Generally, don't implement this, implement `BarImpl` instead.)
/// `bar_dyn` must be safe to call under the documented safety preconditions.
pub unsafe trait DynBar {
    /// Safety
    /// `this` must be type-erased raw `RcBorrow<'_, Self>`,
    /// whose target `self` points to.
    unsafe fn dyn_bar(&self, this: *const ());
}

unsafe impl<T: BarImpl> DynBar for T {
    unsafe fn dyn_bar(&self, this: *const ()) {
        BarImpl::bar(RcBorrow::from_raw(this.cast::<T>()))
    }
}

impl<T: ?Sized + DynBar> Bar for T {
    fn bar(self: &Rc<Self>) {
        Self::bar_rc_borrow(self.into())
    }

    fn bar_rc_borrow(this: RcBorrow<'_, Self>) {
        unsafe { this.dyn_bar(RcBorrow::into_raw(this).cast()) }
    }
}
2 Likes

@steffahn : Sorry, I was not being clear. The cost of cloning a Rc is not a big deal.

I wrote the above post only to motivate how I ran into the problem, in response to @jumpnbrownweasel 's question.

And the point of my post was really only that I wanted to convince myself that it can be done to some degree on stable Rust :wink:; and then I felt like why not post the code

(that's also why it was an answer/addition to my own post, nothing else)

2 Likes