Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate an automatic implementation of TryFrom and Into for C-style enums #3600

Open
agreyyy opened this issue Mar 30, 2024 · 5 comments
Open

Comments

@agreyyy
Copy link

agreyyy commented Mar 30, 2024

I would like to propose that the compiler generates an automatic implementation of TryFrom<{Integer}> and Into<{Integer}> for C-style enums. Now I don`t know if they are actually called C-style enums by the community, but I am referring to these types of enums.

enum CStyleEnum {
    Variant1 = 10,
    Variant2 = 20,
    //....
    Variant10 = 100,
}

One cool thing about C-style enums is that you can convert them to any integer type (u8,u16, etc.) using as syntax:

asssert_eq!(CStyleEnum::Variant10 as u8, 100); //this works

However, C-style enums do not auto implement the Into<{Integer}> and TryFrom<{Integer}> traits like other Integers do (i16, u16, etc.), but I think they should. Since C-Style enums are just a subset of all Integers, I think all the common methods of converting them to Integers and converting Integers to C-Style enums except converting Integers to C-Style Enums with the as keyword(example below) should be supported by the compiler.

assert_eq!(100 as CStyleEnum, CStyleEnum::Variant10); //This should NOT work, 
let n1: isize = -100; // Even Though this does
let n2: usize = 100;
assert_eq!(n2 as isize, n1);

Into<{Integer}>

//an automatic implementation of Into<{Integer}> would reuse all the logic defined for the lines below
//impl of Into for C style enums
let into_num = CStyleEnum::Variant2 as u8;
let into_num = CStyleEnum::Variant2 as u16;
let into_num = CStyleEnum::Variant2 as u32;
let into_num = CStyleEnum::Variant2 as u64;
let into_num = CStyleEnum::Variant2 as i8;
let into_num = CStyleEnum::Variant2 as i16;
let into_num = CStyleEnum::Variant2 as i32;
let into_num = CStyleEnum::Variant2 as i64;
let into_num = CStyleEnum::Variant2 as usize;
let into_num = CStyleEnum::Variant2 as isize;

//look at all those automatic conversion implementations, made by the compiler, it would be
//really nice if it went the other way with Into and TryFrom
let variant = CStyleEnum::Variant1;
let into: u8 = variant.into();
assert_eq!(into, 1u8); // I would like for this to work since its logically equal to the above lines using the `as` syntax

I think The auto-impl of Into<{Itegers}> would make sense since the C-style enum explicitly states that you want states to map to some sort of number. Anyways, the most important part of my proposal in the TryFrom<{Integer}> auto-implementation.

TryFrom<{Integer}>

//it would be very, very nice to have an automatic implementation of TryFrom for this type
let try_from = CStyleEnum::try_from(100)
assert_eq!(Ok(CStyleEnum::Variant10), try_from);

//this is much less boilerplate than the following:
impl TryFrom<{u8}> for CStyleEnum {
    type Err = ();
    fn try_from(value: u8) -> Result<Self, Self::Err> {
        match value {
            10 => Ok(CStyleEnum::Variant1),
            20 => Ok(CStyleEnum::Variant2),
            100 => Ok(CStyleEnum::Variant10),
            _ => Err(()) //idk what sort of useful error you can put for this operation since there is only one point of failure
        }
    }
}

Imagine If this CStyleEnum that I`ve been reusing for these code examples has more than 3 variants, or if I had made multiple C-style enums in my codebase, it would be a real eyesore to look at all those manual TryFrom implementations, and a waste of time to write a macro for something so trivial, and probably unnecessary to bring in an external crate with a macro to implement the TryFrom for me like Strum, since the compiler already logically does this for conversions between numbers, e.g. this is What a TryFrom would look like for u8:

//logically, this boilerplate is very similar
//im sure the std does this some different way, but this is probably one of the ways it could
impl TryFrom<i8> for u8 {
    type Error = ();
    fn try_from(value: i8) -> Result<Self, Self::Error> {
        match value {
            value if value > 0 => Ok(transmute(value)) //some sort of bitshifting to turn the i8`s internal
                                                        //bits into the bits of a u8
            _otherwise => Err(())
        }            
    }
}

The drawbacks of this would be if for some reason, the developer would want a different TryFrom implementation for their C-Style enum, but maybe the rust compiler could let the developer implement it themselves, like an ovveride, or instead the developer could opt-in to the compilers implementation via a #[derive(TryFrom)] for their C-Style Enum.

Conclusion

Anyways, this is just a small Quality Of Life change for Rust, which abstracts a bit of boilerplate away from the developers (us), therefore making our experience better, I think the magnitude of this change is similar to adding a (#[derive(Default)] for enums)[https://github.com//pull/3107]

@agreyyy
Copy link
Author

agreyyy commented Mar 30, 2024

dk if I should turn this into an RFC or not, I was working with C-Style Enums for a DNS project and found converting from and to bytes with C-Style Enums to be far too much boilerplate for the complexity of the logic defined by them.

@programmerjake
Copy link
Member

sounds good as long as you have to opt-in, e.g. via #[derive(Into, TryFrom)]

@kennytm
Copy link
Member

kennytm commented Apr 1, 2024

for reference OP submitted an actual RFC at #3604.

@SOF3
Copy link

SOF3 commented Apr 19, 2024

Why do we specifically want Into and TryFrom instead of a dedicated, probably auto trait for these conversions (like the safe-transmute APIs)?

@kennytm
Copy link
Member

kennytm commented Apr 19, 2024

well, for one, those dedicated traits are already proposed in other RFCs 🙃 (AsRepr, FromRepr @ rust-lang/rust#86772#3040, #3046, not yet implemented)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants