Implementing a trait over a type A or Box<A>

I have a trait like this:

trait Acceptor<B> {
    fn accept(&mut self) -> B;
}

Now, it turns out that in a lot of places my code needs to use a
implementation of Acceptor that has to be instantiated late. So,
I have lots of code like this

struct Blah {
    ac: Option<SomeAcceptor<Class>>
}

impl Blah {
    fn method(&mut self) {
        if let Some(ac) = self.ac {
            ac.accept()
        }
    }
}

Now this got a bit tiresome, so I thought to implement Acceptor for Option.

impl<A, B> Acceptor<B> for Option<A>
where
    A: Acceptor<B>,
{
    fn accept(&mut self) -> B {
        match self {
            Some(ac) => todo!(),
            None => todo!(),
        }
    }
}

Now, I can write my method as:

struct Blah {
    ac: Option<SomeAcceptor<Class>>
}

impl Blah {
    fn method(&mut self) {
       ac.accept()
    }
}

This seems to work, but it fails in one instance because I have

struct Foo {
    ac: Option<Box<SomeAcceptor<Class>>
}

I need the Box to avoid a recursive data structure. So, I thought, I
need to implement Acceptor not just for Option<A> where A:Acceptor, but also anything that deferences to an acceptor:

use std::ops::Deref;

impl<A, B> Acceptor<B> for Option<A>
where
    A: Deref<Target = Acceptor<B>>,
{
}

But this tells me that I have to use dyn, which I presume means that
I will get dynamic dispatch for all my calls to Option<B> including
the non Boxed ones.

Am I right? Is there something better to get this working?

Instead of Target = Acceptor<B>, you want Target: Acceptor<B>. You can re-write your impl like this:

impl<A, B> Acceptor<B> for Option<A>
where
    A: Deref, A::Target: Acceptor<B>,

However, you'll still have an error because this provides conflicting impls for types like Option<Box<Foo>>, which could fall under both impls if Box<Foo> and Foo both implement Acceptor.

Instead, you might want to do something like this:

impl<A, B> Acceptor<B> for Box<A>
where
    A: Acceptor<B>,
{
    fn accept(&mut self) -> B {
        (**self).accept()
    }
}

Full example on Playground.

Thanks, I went for the explicit Box implementation which seems to work well.

I am confused about the semantics of Target = Acceptor<B> vs Target: Acceptor<B> though. I am really not clear on the difference between these two.

In one case the parameter has to be a trait object, in the other case it can be any type that implements the trait.

Ah, okay. Thank you!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.