How to nicely match a number to an enum

#1

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.

#2

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",
#3

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 - https://stackoverflow.com/questions/28028854/how-do-i-match-enum-values-with-an-integer

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.

#5

Voilà

2 Likes
#6

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

#7

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
#8

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