How to hold a `Box<dyn MyTrait>` when `MyTrait` has parametrized types?

Viewed 82

I need to hold an option for a runtime-chosen Box<Device>:

device: Option<Box<dyn Device<'a>>>

but the Device trait has associated types:

pub trait Device<'a> {
    type RxToken: RxToken + 'a;
    type TxToken: TxToken + 'a;

I'm getting

the value of the associated types RxToken (from trait virtual_tun::smoltcp::phy::Device), TxToken (from trait virtual_tun::smoltcp::phy::Device) must be specified

Why do I need to specify these types if they're in a box? Whatever object that implements Device that I store in this Box is already gonna have its RxToken and TxToken chosen. I see no reason to have to specify it.

What can I do in this case?

2 Answers

Its not so much the Box but the dyn that allows you to have dynamic types instead of static (just that Box is something that can hold an unsized type).

With that out of the way, you need to specify the associated type because, even though the type of dyn Device<'_> is dynamic, you still need to interact with it in a static way. You get into trouble if the trait accepts or returns an associated type (as is the norm).

Extrapolating your example:

trait RxToken {}
trait TxToken {}

trait Device<'a> {
    type RxToken: RxToken + 'a;
    type TxToken: TxToken + 'a;
    
    fn read(&self) -> Self::RxToken;
    fn write(&self) -> Self::TxToken;
}

fn main() {
    let device: Option<Box<dyn Device<'_>>> = None;
    
    let _rx_token = device.as_ref().unwrap().read();  // ?
    let _tx_token = device.as_ref().unwrap().write(); // ?
}

What would be the type of _rx_token? Its not specified anywhere. It can't be RxToken + 'a because that is a bound, not a type. And it can't be dyn RxToken + 'a because unsized rvalues are not yet supported.

If you need to use a dyn Device<'_> in a completely dynamic way, you can't have associated types. If you still want to have some RxToken and TxToken be dynamic, you'd need more dynamicism, like:

trait Device<'a> {
    fn read(&self) -> Box<dyn RxToken>;
    fn write(&self) -> Box<dyn TxToken>;
}

Why do I need to specify these types if they're in a box? Whatever object that implements Device that I store in this Box is already gonna have its RxToken and TxToken chosen. I see no reason to have to specify it.

Because currently object safety requires explicitly provided concrete associated types.

There was a pre-rfc to improve that but it apparently went nowhere.

What can I do in this case?

Either specify those types or do something else.

Related