Cannot convert a impl trait to a generic type

#[derive(Default)]
pub struct Builder<E> {
    pub tester_executor: Option<E>,
}

impl<E> Builder<E>
where
    E: FnMut() -> Option<()>,
{
    pub fn tester_executor(mut self, tester_executor: E) -> Self {
        self.tester_executor = Some(tester_executor);
        self
    }
}

pub struct ExecutorPlugin<T> {
    pub pattern: Option<String>,
    pub tester: T,
}

pub trait Plugin {
    fn add_plugin<E>(self, builder: Builder<E>) -> Builder<E>
    where
        E: FnMut() -> Option<()>;
}

pub trait Executor {
    fn get_tester_executor(&self) -> impl FnMut() -> Option<()>;
}

impl<T: Executor> Plugin for ExecutorPlugin<T> {
    fn add_plugin<E>(mut self, builder: Builder<E>) -> Builder<E>
    where
        E: FnMut() -> Option<()>,
    {
        builder
            .tester_executor(self.tester.get_tester_executor())
    }
}

The impl trait returned from get_tester_executor cannot be passed to tester_executor which has a generic type.

error[E0308]: mismatched types
  --> examples/bug.rs:37:30
   |
32 |     fn add_plugin<E>(mut self, builder: Builder<E>) -> Builder<E>
   |                   - expected this type parameter
...
37 |             .tester_executor(self.tester.get_tester_executor())
   |              --------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `E`, found associated type
   |              |
   |              arguments to this method are incorrect
   |
   = note: expected type parameter `E`
             found associated type `impl FnMut() -> Option<()>`
help: the return type of this call is `impl FnMut() -> Option<()>` due to the type of the argument passed
  --> examples/bug.rs:36:9
   |
36 | /         builder
37 | |             .tester_executor(self.tester.get_tester_executor())
   | |______________________________---------------------------------^
   |                                |
   |                                this argument influences the return type of `tester_executor`
note: method defined here
  --> examples/bug.rs:10:12
   |
10 |     pub fn tester_executor(mut self, tester_executor: E) -> Self {
   |            ^^^^^^^^^^^^^^^           ------------------

how to solve it ?

The problem with a generic parameter here is that the caller gets to choose E, not the callee (i.e. your method implementation). What we need to do here is propagate the E parameter such that self.tester.get_tester_executor() returns E instead of some impl FnMut() -> Option<()>. Here is one possible way:

#[derive(Default)]
pub struct Builder<E> {
    pub tester_executor: Option<E>,
}

impl<E> Builder<E>
where
    E: FnMut() -> Option<()>,
{
    pub fn tester_executor(mut self, tester_executor: E) -> Self {
        self.tester_executor = Some(tester_executor);
        self
    }
}

pub struct ExecutorPlugin<T> {
    pub pattern: Option<String>,
    pub tester: T,
}

pub trait Plugin<E> {
    fn add_plugin(self, builder: Builder<E>) -> Builder<E>;
}

pub trait Executor<E> {
    fn get_tester_executor(&self) -> E;
}

impl<E: FnMut() -> Option<()>, T: Executor<E>> Plugin<E> for ExecutorPlugin<T> {
    fn add_plugin(mut self, builder: Builder<E>) -> Builder<E>
    {
        builder
            .tester_executor(self.tester.get_tester_executor())
    }
}

Usually I'd share a playground alongside the snippet, but looks to me like the playground is broken right now.

2 Likes

Thanks a lot ~ :grin:
You corrected a misconception I had.

1 Like