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:
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 Size
d 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
)
AsRef<Listener>
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);
}
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
{
}