2

I have a method that I need to call with a trait parameter (let's call it Listener). The reason is that sometimes I have previously stored this trait parameter into a parent structure so it is inside a Box, and sometimes not.

So I have the two methods :

  • fref<T>(t: &T) where T: Listener
  • fbox(t: &Box<dyn Listener>)

and I would like both of them to call f(t: ??). For now I duplicated the code in fref and fbox which works but is not good. So I am looking for a signature of f that would make it callable from fref and fbox to factorize my code. I was hoping one of the traits implemented by Box would be equivalent to a & (or at least find a common ground somewhere).

I tried the following:

  • Writing f<T>(t: &T) where T: Listener but then I can't call from fbox (Listener is not implemented by Box<dyn Listener>).
  • Then changing the call from within fbox to f(&*t) to unbox my Box<Listener> but since t is not Sized I can't.

  • Writing f<T>(t: &T) where T: std::borrow::Borrow<Listener> but then I can't call from fref (Borrow is not implemented by Listener)

  • Same with AsRef<Listener>
  • Last attempt with Deref playground:
trait Listener {}
struct Mouse {}
impl Listener for Mouse {}

fn fbox(t: &Box<Listener>) {
    f(t);
}

fn fref<T>(t: &T)
where
    T: Listener,
{
    f(t);
}

fn f<T>(_t: &T)
where
    T: std::ops::Deref<Target = Listener>,
{

}

fn create_listener() -> impl Listener {
    Mouse {}
}

fn main() {
    let mouse = create_listener();
    let box_mouse: Box<Listener> = Box::new(Mouse {});

    fref(&mouse);
    fbox(&box_mouse);
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
jolivier
  • 7,380
  • 3
  • 29
  • 47
  • 1
    Both `&T` and `Box` implement `Deref` with `Target = T`. Is that what you are after? It's a bit unclear because you didn't provide a [mcve]. – Peter Hall Mar 01 '19 at 17:35
  • I have slightly improved the [Playground](https://play.integer32.com/?version=stable&mode=debug&edition=2018&gist=67fc2cbe2c9f4c8476219497a580da8c) example from @Stargateur – wigy Mar 01 '19 at 17:47
  • ok I added a mcve based on @Stargateur with the question naming and an attempt at Deref – jolivier Mar 01 '19 at 17:51
  • It's because `Listener` is a trait. The box is actually a trait object `Box`, while the other is a regular type parameter. – Peter Hall Mar 01 '19 at 17:59
  • I don't thank you can because in one you try to use a static dispatch function with a dynamic dispatch data. But I probably miss something – Stargateur Mar 01 '19 at 18:17
  • *`Listener` is not implemented by `Box`* — Why don't you implement it then? – Shepmaster Mar 01 '19 at 20:13
  • @Shepmaster well for me this is the point of ```Deref``` implemented by ```Box```, but I guess the ```dyn``` prevents this – jolivier Mar 02 '19 at 14:19

1 Answers1

4

Listener is a trait, so Box<Listener> is really a trait object, Box<dyn Listener> - it is unfortunate that the dyn keyword is currently optional. Both Box<dyn Listener> and &Mouse implement Deref, with an associated Target type that implements Listener. In the case of &Mouse the deref Target is Mouse, but in the case of Box<dyn Listener> it is an unknown object, dyn Listener of unknown size.

To capture all of that information, you can write f like this:

fn f<T, L>(_listener: &T)
where
    T: Deref<Target = L>,
    L: Listener + ?Sized
{
}

And call it from each function like this:

fn fbox(listener: &Box<dyn Listener>) {
    f(listener);
}

fn fref<L>(listener: &L)
where
    L: Listener
{
    f(&listener);
}

Another, perhaps simpler way of looking at this is to forgo the Deref constraint and just use normal references. Use Box::as_ref to turn a Box in to a reference in order to call it. The ?Sized un-constraint is still needed for the trait object case, and still works since the value is always behind a pointer:

fn fbox(listener: &Box<dyn Listener>) {
    f(listener.as_ref());
}

fn fref<L>(listener: &L) where L: Listener {
    f(listener);
}

fn f<L>(_listener: &L)
where
    L: Listener + ?Sized
{
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • 1
    I think I finally understand what is the use of `?Sized` – Stargateur Mar 01 '19 at 23:21
  • is this mean `f` use dynamic dispatch ? Or as it's generic, the dynamic dispatch is only when `f` is used with `box` ? – Stargateur Mar 01 '19 at 23:24
  • @Stargateur It will use dynamic dispatch for the boxed trait object, but not for the known type. Remember that `f` will get monomorphised for each possible `T`. – Peter Hall Mar 02 '19 at 13:14