Idiomatic way use a dyn Trait to accept multiple generic types

Hey rustaceans!
I'm having a hard time to figure it out what is the idiomatic way to do the following.

mod ent {
    pub trait Controller {}

    pub struct LocalController {}
    pub struct RemoteController {}

    impl Controller for LocalController {}
    impl Controller for RemoteController {}
}

// This is intentional to cross the vehicle types
pub trait Vehicle<C>
where
    C: ent::Controller,
{
}

pub trait FlyingVehicle<C>: Vehicle<C>
where
    C: ent::Controller,
{
}

pub trait LandVehicle<C>: Vehicle<C>
where
    C: ent::Controller,
{
}

pub struct LocalTrain {}
pub struct RemoteTrain {}
pub struct LocalPlane {}
pub struct RemotePlane {}

impl Vehicle<ent::LocalController> for LocalTrain {}
impl LandVehicle<ent::LocalController> for LocalTrain {}

impl Vehicle<ent::LocalController> for LocalPlane {}
impl FlyingVehicle<ent::LocalController> for LocalPlane {}

// ... (other impl)

fn main() {
    let veh = Box::new(LocalPlane {});
    working_function(veh); // This is working but not dynamic
    dynamic_function(veh); // Error: the trait bound `LocalPlane: FlyingVehicle<Box<(dyn Controller + 'static)>>` is not satisfied
}

// This is where I'm stuck (How to accept all flying vehicles? Local or Remote)
fn dynamic_function(vehicle: Box<dyn FlyingVehicle<Box<dyn ent::Controller>>>) {}

fn working_function<T>(vehicle: Box<dyn FlyingVehicle<T>>)
where
    T: ent::Controller,
{

}

Did what I was doing make sense?
Is it possible to do that ( accept all flying vehicles? Local or Remote, inside the dynamic_function )

I tried to implement the trait ent::Controller for Box, is it the right thing to do ?

Is FlyingVehicle<T> implement for LocalPlane with any <T> besides LocalController? You can only go from Box<LocalPlane> to Box<dyn FlyingVehicle<T>> if LocalPlane implements FlyingVehicle<T>.

More generally I suspect some confusion between generics and dyn Trait, but I'm not completely sure, and it's unclear to me why you have the specific function signatures that you do. dyn Trait is a concrete type, despite being dynamically sized and dispatched.

The compiler supplies an implementation of Trait for dyn Trait, but not for other types, so you can supply implementations like

    impl Controller for Box<dyn Controller + '_> {}
    impl Controller for Box<dyn Controller + Send + '_> {}
    impl Controller for Box<dyn Controller + Send + Sync + '_> {}

but that still won't make dynamic_function callable unless LocalPlane implements FlyingVehicle<Box<dyn Controller>>.

If it does, then your signature can be satisfied.

It is because I want to accept every flying vehicle at runtime, is it the right way to do it?

fn any_flying<V, T>(vehicle: V)
where
    V: Vehicle<T>,
    T: ent::Controller,
{}

It's more general than

fn any_flying(
    vehicle: Box<dyn FlyingVehicle<Box<dyn ent::Controller + '_>> + '_>
)
{}

And doesn't require Box<Controller + '_>: Controller (though you probably want those anyway) and doesn't lead to making three variants for the + Send + Sync like cases.

For example if I need to call a callback that accepts any flying, I don't think I can use compile-time generic.

The following doesn't work, I think there is something I don't understand here:

struct Observer {
    cb: Box<dyn FnMut(Box<dyn FlyingVehicle<Box<dyn ent::Controller>>>)>,
}

EDIT: Also in my case I can't add generic parameters to my Observer

It works if you implement Controller for Box<dyn Controller>.

Or without doing that, this may or may not make sense depending on how everything else works.

struct Observer {
    cb: Box<dyn FnMut(Box<dyn FlyingVehicle<dyn ent::Controller>>)>,
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.