`=> Box<dyn Trait>` for generic trait

Hello,

i am trying to implement CLI for a specific request/response protocol.
All the below types & functions are my creation and members of crate, and can be modified/changed as needed to fit the use-case properly...

I have following trait and it's implementations for various types:

pub trait NetconfRequest {
    type Response: NetconfResponse + Debug;
    ...
}

impl NetconfRequest for HelloRequest {
    type Response = HelloResponse;
    ...
}

impl NetconfRequest for LockRequest {
    type Response = LockResponse;
    ...
}

Other part of program parses user input into non generic CLI command structs/enum, and i'd like to map this into corresponding above trait implementation.

I was thinking of something like this:

pub enum NonGenericCommand { 
    HelloCommand(...),
    LockCommand(...),
}

impl NonGenericCommand {
    pub fn to_request<R: NetconfRequest>(
        &self,
        message_id: String,
    ) -> Box<dyn NetconfRequest<Response = R::Response>> {
        match self {
            NonGenericCommand::HelloCommand(...) => Box::new::<HelloRequest>(HelloRequest::new(...))
            NonGenericCommand::LockCommand(...) => Box::new::<LockRequest>(LockRequest::new(...))
        }
    }
}

But i fail (sort of expectedly) with following error, which is rather clear, but does not fit my expectation of being able to return Box dyn of generic type...

error[E0271]: type mismatch resolving `<HelloRequest as NetconfRequest>::Response == <R as NetconfRequest>::Response`
   --> netconf-cli\src\commands\clap_commands.rs:103:17
    |
103 |                 Box::new::<HelloRequest>(req)
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found struct `HelloResponse`
    |
    = note: expected associated type `<R as NetconfRequest>::Response`
                        found struct `HelloResponse`
    = note: required for the cast to the object type `dyn NetconfRequest<Response = <R as NetconfRequest>::Response>`
help: consider constraining the associated type `<R as NetconfRequest>::Response` to `HelloResponse`
    |
91  |     pub fn to_request<R: NetconfRequest<Response = HelloResponse>>(
    |                                        ++++++++++++++++++++++++++

Recommended constraining is not "acceptable" due to my attempt to use bo dyn return value.
Can generic Trait be used in Box dyn return value somehow?

When a function has a type parameter R, the caller gets to choose R (within the trait bounds), and it's fixed for any given invocation. So you can't return different Rs (or projections into R like R::Response) from the function.

You could type erase the response:

trait NetconfResponseAndMore: NetconfResponse + Debug {}
impl NetconfResponse for Box<dyn NetconfResponseAndMore> {}
impl<T: NetconfResponse + Debug> NetconfResponseAndMore for T {}

struct BoxResponder<T>(T);
impl<T: NetconfRequest> NetconfRequest for BoxResponder<T> {
    type Response = Box<dyn NetconfResponseAndMore>;
}

And then have that be part of your type-erased return:

    fn to_request(
        &self,
        message_id: String,
    ) -> Box<dyn NetconfRequest<Response = Box<dyn NetconfResponseAndMore>>> {
        match self {
            NonGenericCommand::HelloCommand() =>
                Box::new(BoxResponder(HelloRequest {})),
            NonGenericCommand::LockCommand() =>
                Box::new(BoxResponder(LockRequest {})),
        }
    }

Playground.

It's similar to this situation.

1 Like

Thank you for detailed sample implementation!

I need to go through it few times and build the whole picture in my head, though most of the code seems to be clear & makes sense.