Data marshalling w/ Trait and dyn types in an enum

I have a trait like this

pub trait Pt<'a>: Sync {
    fn is_int(&self) -> bool;
    fn is_dynamic_string(&self) -> bool;
    fn max_length(&self) -> u8;
    fn signed(&self) -> bool;
    fn encode(&self, out: &mut Vec<TokenValue>, v: TokenValue) {}
    #[allow(overflowing_literals)]
    fn parse(&self, s: &'a str, mut pos: usize) -> (TokenValue, usize) {}
}

Where actual type classes are applied, here is an example one

#[derive(Ord, PartialOrd, Debug, Eq, Hash, PartialEq, Clone, Copy)]
struct PtUint32 {}
impl<'a> Pt<'a> for PtUint32 { ...}

Then I have an encompassing Enumeration type that satisfies the above trait but can also contain one of the other types that implement the same trait.

#[derive(Clone, Ord, PartialOrd)]
struct Enumeration<'a> {
    pt: &'a dyn Pt<'a>,
    max_length: u8,
    enum_name: &'a str,
    enums: BTreeMap<&'a str, TokenValue<'a>>,
    reverse_enums: BTreeMap<TokenValue<'a>, &'a str>,
}

Compile fails. What is the compiler telling me?

error[E0277]: the trait bound `dyn Pt<'a>: Ord` is not satisfied
   --> src/msgproto.rs:342:5
    |
340 | #[derive(Clone, Ord, PartialOrd)]
    |                 --- in this derive macro expansion
341 | struct Enumeration<'a> {
342 |     pt: &'a dyn Pt<'a>,
    |     ^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `dyn Pt<'a>`
    |
    = note: required for `&dyn Pt<'a>` to implement `Ord`
    = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: can't compare `&dyn Pt<'a>` with `&_`
    --> src/msgproto.rs:342:5
     |
340  | #[derive(Clone, Ord, PartialOrd)]
     |                      ---------- in this derive macro expansion
341  | struct Enumeration<'a> {
342  |     pt: &'a dyn Pt<'a>,
     |     ^^^^^^^^^^^^^^^^^^ no implementation for `&dyn Pt<'a> == &_`
     |
     = help: the trait `PartialEq<&_>` is not implemented for `&dyn Pt<'a>`
note: required by a bound in `core::cmp::PartialOrd::partial_cmp`
    --> /Users//.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/cmp.rs:1082:43
     |
1082 | pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
     |                                           ^^^^^^^^^^^^^^ required by this bound in `PartialOrd::partial_cmp`
...
1108 |     fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;
     |        ----------- required by a bound in this associated function
     = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: can't compare `dyn Pt<'a>` with `_`
   --> src/msgproto.rs:342:5
    |
340 | #[derive(Clone, Ord, PartialOrd)]
    |                      ---------- in this derive macro expansion
341 | struct Enumeration<'a> {
342 |     pt: &'a dyn Pt<'a>,
    |     ^^^^^^^^^^^^^^^^^^ no implementation for `dyn Pt<'a> < _` and `dyn Pt<'a> > _`
    |
    = help: the trait `PartialOrd<_>` is not implemented for `dyn Pt<'a>`
    = note: required for `&dyn Pt<'a>` to implement `PartialOrd<&_>`
    = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)

Is the problem that you can't Ord or PartialOrd on a trait? It has to be a dynamic type?

The derive macro works by comparing field-by-field. dyn Pt doesn't implement Ord or PartialOrd, so the derivation can't succeed.

The compiler isn't going to provide an arbitrary comparison between type-erased values like dyn Pt. You can accomplish it via downcasting in some cases, but

  • It makes mores sense for PartialOrd than Ord unless you choose some arbitrary base-type agnostic total ordering which may result in odd behavior elsewhere
  • The approach in that example requires Ty: 'static, which it looks like you do not have

You could consider working something into your trait that makes comparisons possible between any two implementors, maybe.

You could write your own implementation and choose some particular (again, perhaps odd) behavior when you have two type-erased operands, maybe. Might be a useful exercise to see what the challenges are.

I have no idea what you're asking here.


Unrelated side-note: I suggest using #![deny(elided_lifetimes_in_paths)] to make it more clear where borrows are happening and what is borrowing from what.

2 Likes

I follow what you are saying. In fact differentiating could be done as described here. Thanks!

Yes, I need dynamic types. It isn't data marshalling without it. (That is, encoding/decoding and transferring data from one system to another)

You CAN'T have a dynamic Trait reference because there is no object to be dynamic. So, I deal with this by encapsulating all of the types that use this trait into an enum, like this :

#[derive(Ord, PartialOrd, Eq, PartialEq, Hash)]
enum PtTypes {
    UINT32(PtUint32),
    INT32(PtInt32),
    UINT16(PtUint16),
    INT16(PtInt16),
    BYTE(PtByte),
    STRING(PtString),
    PROGMEMBUFFER(PtProgmemBuffer),
    BUFFER(PtBuffer),
    ENUM(Enumeration<'static>),
}

And then provide an accessor method :

impl<'a> PtTypes {
    fn object(&self) -> &dyn Pt<'a> {
        match self { ... }

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.