Arbitrary enums with explicit discriminants

Hello

I needed this feature but I did not want to use nightly so I made a crate that kind of emulates it. Here it is: variant.rs. It is not published yet and probably won't be. I would like opinions about the proc-macro and the uses of unsafe.

It would be nice to hear the motivation. Apart from the explicit discriminants, it seems the feature you add is a type-based new and get method. Your use of unreachable! is incorrect, because that branch is certainly reachable.

I haven't looked carefully at the unsafe code, since I don't see a reason for it. What is the value of an explicit discriminant?

It uses a union internally so it needs unsafe when reading from it.

You are right, the unreachable statement was reachable in the constructor is called with an invalid type parameter. I fixed it. Thank you

I find the generic-type based API a bit weird: what happens if two variants share the same type?

I think that adding a constructor for each branch can make it look quite nice. Then, you can try to add some sugar to make pattern-matching more user-friendly. Here is a basic PoC of the idea:

arbitrary_discriminants! {
    #[repr(u8)]
    /// hi
    pub(self)
    enum Foo {
        _Bar,
        Baz(i32) = 42,
        _Quux,
    }
}

fn main ()
{
    let f = Foo::Baz(42);
    match_! { f,
        Foo::_Bar => {},
        Foo::Baz(ft) => println!("{}", ft),
        Foo::_Quux => {},
    }
}
Expansion
/// hi
pub(self) struct Foo {
    pub tag: FooTag,

    pub payload: FooPayload,
}

#[allow(unused_parens)]
impl Foo {
    #[allow(nonstandard_style, unused_parens)]
    pub fn _Bar() -> Self {
        Self{tag: FooTag::_Bar, payload: FooPayload{_Bar: { },},}
    }
    #[allow(nonstandard_style, unused_parens)]
    pub fn Baz(it: i32) -> Self {
        Self{tag: FooTag::Baz, payload: FooPayload{Baz: { let _: i32; it },},}
    }
    #[allow(nonstandard_style, unused_parens)]
    pub fn _Quux() -> Self {
        Self{tag: FooTag::_Quux, payload: FooPayload{_Quux: { },},}
    }
}

#[repr(u8)]
pub(self) enum FooTag { _Bar, Baz = 42, _Quux, }

#[allow(nonstandard_style, unused_parens)]
pub(self) union FooPayload {
    _Bar: (),
    Baz: (i32),
    _Quux: (),
}
fn main() {
    let f = Foo::Baz(42);
    match f {
        e => {
            match e.tag {
                FooTag::_Bar => {
                    #[allow(unused_parens)]
                    let () = unsafe { e.payload._Bar };
                    { }
                }
                FooTag::Baz => {
                    #[allow(unused_parens)]
                    let (ft) = unsafe { e.payload.Baz };
                    println!("{}", ft)
                }
                FooTag::_Quux => {
                    #[allow(unused_parens)]
                    let () = unsafe { e.payload._Quux };
                    { }
                }
            }
        }
    }
}

Thank you for your answer. I made a new version inspired by your suggestion: link. I went with macro_rules instead of proc_macro, I think it was overkill.

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.