Search code examples
rusttraits

Using dyn trait with a hierarchy of marker traits


I'm using the open source rust pcap library (https://github.com/rust-pcap/pcap) whose main Capture struct uses a system of market traits to denote which functions are available during which lifecycle state/phase of the capture (e.g., you can't 'sendpacket' on a Capture in the "DEAD" state). It has a hierarchy of State traits such that, e.g., "Activated" is a trait that is implemented by both the "Active", "Dead" and "Offline" states:

pub trait Activated: State {}
impl Activated for Active {}
impl Activated for Offline {}
impl Activated for Dead {}
pub trait State {}
impl State for Inactive {}
impl State for Active {}
impl State for Offline {}
impl State for Dead {}

pub struct Capture<T: State + ?Sized> {
...
}

With that code, I am trying unsuccessfully to convert from a parameterized+bounded trait as a <T: Activated + ?Sized> to a Capture, e.g., :

fn foo<T: Activated + ?Sized>(&mut capture: Capture<T>) {
    bar(capture as Capture<dyn Activated>);
}

fn bar(&mut capture: Capture<dyn Activated>) {
 ...
}

... but am getting an error:

non-primitive cast: &mut pcap::Capture<T> as &mut pcap::Capture<dyn pcap::Activated> as expression can only be used to convert between primitive types or to coerce to a specific trait object

... which I don't understand at all, despite a fair bit of reading on this. Can someone please explain to me how to do this (or why it's wrong to think this should work)? Thank you in advance!


Solution

  • In current Rust, you can only coerce from Foo<T> to Foo<dyn Trait> if T is a sized type. This is because the conversion requires attaching a vtable pointer for Trait, and said vtable is only available when T is a regular, non-dyn type. So, this code,

    fn foo<T: Activated + ?Sized>(&mut capture: Capture<T>) {
        bar(capture as Capture<dyn Activated>);
    }
    

    will always fail to compile because T: ?Sized, meaning that T might be already a dyn, and you can't cast from a dyn to an arbitrary different dyn.

    There is an unstable feature, trait_upcasting (#65991) that will enable dyn conversions to supertraits, like Capture<dyn Activated> to Capture<dyn State>, but that still does not include conversion from an arbitrary T: ?Sized. (In fact, if T = [Foo], a type that is dynamically sized for a different reason, then it's impossible for an entirely different reason than traits — the compiler does not support a type being simultaneously vtable-based (having a pointer) and a slice (having a length). That restriction could in principle be lifted, but it'd be more new territory.)

    To recap: You will need to organize your program so that you do not need to convert a ?Sized type to a trait object, because that is not possible.