rust-lang / rfcs

RFCs for changes to Rust
https://rust-lang.github.io/rfcs/
Apache License 2.0
5.78k stars 1.55k forks source link

RFC: Add a special TryFrom and Into derive macro, specifically for C-Style enums #3604

Open agreyyy opened 3 months ago

agreyyy commented 3 months ago

I think this a good quality of life proposal similar to the proposal to add a derive Default for enums, not big in scope or magnitude. TLDR: I want to make a derive macro for TryFrom and Into, that generate an impl of TryFrom and Into for any C-Style Enum:

//using these two macros
#[derive(FromInt, IntoInt)] // names subject to change
enum CStyleEnum {
   Variant1 = 10,
   Variant2 = 20,
   ...
}

//convert from a number to the C-style enum easily
let from_num = CStyleEnum::try_from(10); // Ok(CStyleEnum::Variant1)
let failed_from = CStyleEnum::try_from(21); // Err(())
//convert into a number from a C-style enum more generically
let into_num = CStyleEnum::Variant2.into(); //20

Rendered

kennytm commented 3 months ago

Note that the RFC itself can already been done with the num_enum package (and tons of other alternatives).

---
[dependencies]
num_enum = "0.7.2"
---

use num_enum::{TryFromPrimitive, IntoPrimitive, TryFromPrimitiveError};

#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Debug)]
#[repr(i32)]
enum CStyleEnum {
    Variant1 = 10,
    Variant2 = 20,
}

fn main() {
    assert_eq!(CStyleEnum::try_from(10), Ok(CStyleEnum::Variant1));
    assert_eq!(CStyleEnum::try_from(21), Err(TryFromPrimitiveError { number: 21 }));
    assert_eq!(i32::from(CStyleEnum::Variant2), 20);
}

Unlike #3107 about #[derive(Default)], the standard library did not have #[derive(TryFrom, Into)] already.


For non-C-style enums, the derive_more package provided #[derive(From, TryInto)] which is based on shape rather than discriminant.

#[derive(From, Debug)]
#[repr(i32)]
enum NotReallyCStyleEnum {
    A = 10,
    B(i32) = 20,
}

fn main() {
    dbg!(NotReallyCStyleEnum::try_from(10_i32)); // B(10), not A
}
clarfonthey commented 3 months ago

While I would like to see From and TryFrom derives supported by the standard library, I have to admit that so many crates exist to do this and it's going to take a very robust proposal to accomplish this.

This isn't saying that your proposal is bad, genuinely, just that the bar is extremely high for this IMHO. Since we can't really change what the shape of the standard library version would be once stabilised, we're going to need a very solid crate implementing exactly what form this should take, and see it widely used before any standard library version is likely to get added.

I like derive_more, but as mentioned, its functionality is split with stuff like num_enum, and it's not clear how From and TryFrom should be derived in all cases. Again, we need a very comprehensive proposal to make it in the standard library, and right now we just have several perfectly reasonable proposals who aren't as comprehensive, which is fine since they're in separate crates and people can use what they need.

ehuss commented 3 months ago

I'm tentatively marking this t-libs-api, but I don't actually know if that's the right team assignment.

It does seem like there is a lot of background and prior-art that isn't mentioned in the RFC. For example, this has been brought up in https://github.com/rust-lang/rfcs/issues/2783 and https://internals.rust-lang.org/t/derive-tryfrom-for-c-like-enums/12940 among other places.

matklad commented 2 months ago

See also https://github.com/rust-lang/rust/issues/86772