Cannot imp block into a trait default using enum casting

Hi everyone,

I wanted to turn an impl funtion into a default function for a Trait. This seems not to work for enum as u8

working example

#[repr(u8)]
pub enum SimpleEnum {
    One,
    Two,
}

impl SimpleEnum {
    pub fn rank(self) -> u8 {
        self as u8 + 1
    }
}

fn main() {
    let a = SimpleEnum::One;
    assert_eq!(a.rank(), 1);
}

not working

#[repr(u8)]
pub enum SimpleEnum {
    One,
    Two,
}

pub trait Simple
where
    Self: Sized,
{
    fn rank(self) -> u8 {
        self as u8  + 1
    }
}

impl Simple for SimpleEnum {}

fn main() {
    let a = SimpleEnum::One;
    assert_eq!(a.rank(), 1);
}

This gives me the following error:

error[E0605]: non-primitive cast: `Self` as `u8`
  --> src/main.rs:11:9
   |
11 |         self as u8  + 1
   |         ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

For more information about this error, try `rustc --explain E0605`.
error: could not compile `enumtrait` (bin "enumtrait") due to 1 previous error

I looked at the rustc --explain E0605 and the reference guide, but that did not help me to understand why the impl fn accepts self as u8 + 1, but the Trait not.

I am relatively new to Rust. So I probably didn't quite understand the things that I red.

Is there anyone who can explain the error message in simple terms and why it works in the implfn and not in the Trait definition?

Thanx in advance.

A trait can be implemented for a range of different types. Your default implementation for rank() trait method assumes it is safe to convert to u8 from type T that trait Simple is implemented for. The compiler can't guarantee this.

When you move implementation details of rank() method to specialized type SimpleEnum it should work as expected:

#[repr(u8)]
pub enum SimpleEnum {
    One,
    Two,
}

pub trait Simple
where
    Self: Sized,
{
    fn rank(self) -> u8;
}

impl Simple for SimpleEnum {
    fn rank(self) -> u8 {
        self as u8 + 1
    }
}

fn main() {
    let a = SimpleEnum::One;
    assert_eq!(a.rank(), 1);
}

Playground link

This is just a try (source). But seemingly a dangerous try from the previous answer, since it'd be UB likely for most implementors?

    fn rank(self) -> u8 {
        self.discriminant() + 1
    }
        fn discriminant(&self) -> u8 {
        unsafe { *(self as *const Self as *const u8) }
    }

I wrote this library for exactly that use case:

1 Like

Thanks guys.

It seems to me that the statement

A trait can be implemented for a range of different types. Your default implementation for rank() trait method assumes it is safe to convert to u8 from type T that trait Simple is implemented for. The compiler can't guarantee this.

from burumdev is correct.

I tried the crate repr-discriminantfrom Schard in verious ways, but I was unable to get that functionality working as a default Trait function.

cheers, Willem

That's because it does not ship a trait for it yet, because traits cannot have const functions and I needed those (and not a trait). I'd be happy though, to add a trait, if so desired.

Update: Version 2 now ships a trait and the derive macro via the derive feature:

https://crates.io/crates/repr-discriminant