C-style enum conversions


#1

Sometimes in code I use repr() on C-style enums:

#[repr(u8)]
enum MyEnum { A = b'A', B = b'B', C = b'C' }

And sometimes in such code I need to convert the base type (here u8) from and to the enum (here MyEnum).

You can convert MyEnum => u8 with a simple “as” cast:

let e1 = MyEnum::B;
let x = e1 as u8;

But the wise person knows that in Rust code the number of “as” should be minimized, because they are bug-prone.
And std::mem::discriminant is not meant to solve this problem.

One way to solve this problem is to support the base type from():

let x = u8::from(e1);

A possibly better but less standard solution is to give enums a conversion method:

let x = e1.as_base_value();
assert_eq!(x, b'B');

A problem is that such method should be available only for C-like enums.


#2

How is e1 as u8 more error prone than u8::from(e1)?


#3

In general in Rust “as” is bug-prone, so in general you should minimize the number of times you use “as” in your Rust code.

In the specific problem I’m discussing in this post u8::from(e1) is safer because if you later change the code like this:

#[repr(u16)]
enum MyEnum { A = b'A', B = b'B', C = b'C' }
let e1 = MyEnum::B;
let x = u8::from(e1); // Compilation error.

The from() will not compile, because the base type of MyEnum isn’t u8 any more. from() performs lossless conversions only. The method as_base_value() is even better because it knows the base type, while this (sloppily) compiles because a u8 base type fits into an u16:

#[repr(u8)]
enum MyEnum { A = b'A', B = b'B', C = b'C' }
let e1 = MyEnum::B;
let x = u16::from(e1); // Compiles.
let x: u16 = e1.as_base_value(); // Compilation error.

#4

Not disagreeing that a “base value” might be useful, but why is widening a u8 to u16 a concern?


#5

Not disagreeing that a “base value” might be useful, but why is widening a u8 to u16 a concern?

The u8=>u16 is not a concern for me, that’s why in the first post of this thread I have suggested the T::from() as an acceptable solution for this problem. A method that gives the exact type is more precise and more precision is sometimes good and sometimes unnecessary/overkill.

That the presence of “as” casts in Rust code should be minimized is not yet stressed enough in Rust books, documentation, standard library docs, and not yet supported in the standard library features, so some Rust programmers are still kind of blind at this problem: https://github.com/isislovecruft/curve25519-dalek/issues/73