How to nicely match a number to an enum

I have some code that needs to match a numeric u32 to a scalar enum and my code looks like this:

let browse_name = match id {
    id if id == ReferenceTypeId::References as u32 => "References",
    id if id == ReferenceTypeId::NonHierarchicalReferences as u32 => "NonHierarchicalReferences",
    id if id == ReferenceTypeId::HierarchicalReferences as u32 => "HierarchicalReferences",
    id if id == ReferenceTypeId::HasChild as u32 => "HasChild",
    // etc.
};

The full code is here - https://github.com/locka99/opcua/blob/master/types/src/relative_path.rs#L136

This looks really ugly. I suppose I could hide this with a macro, but is there an easier way to do this safely, or a more idiomatic way? It seems like that this is common enough requirement, especially when dealing with data out of streams.

Can't you do match id in the native type of the enum in order to avoid having computed match arms (and thus guard conditions as patterns can't be computed) ?

I mean

let browse_name = match id as something {
      ReferenceTypeId::References => "References",

I don't think so since while ReferenceTypeId is a scalar enum, it is a subset of u32 so not every u32 is going to match to something.

I took my solution from this stack overflow answer - rust - How do I match enum values with an integer? - Stack Overflow

In an ideal world there would be a macro in std where I could just write "let id = match_to_scalar_enum!(id, ReferenceTypeId)" and id would hold a Result with the matching enum or an error.

VoilĂ 

2 Likes

Great solution! I think you should put this in a crate so others could use it.

I have a crate for this: https://github.com/dtolnay/enumn

use enumn::N;

#[derive(N)]
enum ReferenceTypeId {
    References,
    NonHierarchicalReferences,
    HierarchicalReferences,
    HasChild,
}

let id: u32 = /* ... */;
match ReferenceTypeId::n(id) {
    Some(ReferenceTypeId::Reference) => /* ... */,
    /* ... */
    None => /* ... */,
}
1 Like

There is also num-derive. Usage is similar to enumn above, though in place of #[derive(N)] you derive FromPrimitive and in place of Type::n it should be Type::from_u32 (given that FromPrimitive trait from num-traits is in scope). I find that easier to read then single-letter names.

3 Likes