4 ms·
It's not really an enum if you can't enumerate all the values at compile time. Rust enums are not C enums. I'd represent it like this: #[repr(transparent)
by johnsoft 6y ago
It's not really an enum if you can't enumerate all the values at compile time. Rust enums are not C enums.
I'd represent it like this:
#[repr(transparent)]
struct Foo(i32);
impl Foo {
const A: Foo = Foo(0);
const B: Foo = Foo(1);
const C: Foo = Foo(2);
}
- drran 6y ago1) Enums in C are implemented that way, so C-like enums in Rust must implement it in the same way to be compatible with C. 2) This is required for forward compatibility. For example protobuf explicitly requires it. 3) Your code is not forward compatible. The proper way is to implement it is as i32 and then unwrap it, or: enum Foo { A = 0, B = 1, C = 2, _UNKNOWN_3 = 3, _UNKNOWN_4 = 4, _UNKNOWN_5 = 5, _UNKNOWN_6 = 6, // ... repeat few dozen times, to be forward compatible } Example: https://github.com/apoelstra/rust-bitcoin/blob/c37ab1f9c2392125a492115c1d2c7a8a77cfff36/src/blockdata/opcodes.rs#L415 https://github.com/apoelstra/rust-bitcoin/blob/c37ab1f9c2392... P.S. IMHO, Rust should allow to define enums as: enum Foo { A = 0, B = 1, C = 2, _ , // Unknown values which must be handled by default case } or enum Foo { A = 0, B = 1, C = 2, _OTHER , // Unknown values which must be handled } because current workaround is usable for i8, maybe even for i16, but not for i32.
- pornel 6y agoRust enums are not compatible with C, even with `#[repr(C)]`. Values not explicitly present in the enum are not allowed. Casting them to a Rust enum is UB, and it does actually cause miscompilation, because `match` can be a jump table.
- andrewaylett 6y agoRust has an annotation to mark that an enum is non-exhaustive[1], and a mechanism for declaring an enum as being laid out in a manner compatible with C[2]. I've not tried using them together :). In general, it _is_ possible for any C data layout to be represented in Rust -- but it's not necessarily the case that the representation has the same name. And it's also not the case that we can safely pass memory from C to Rust without validating the content, even if the representation is equivalent for all valid values. [1] https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#non_exhaustive-structs-enums-and-variants https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#non_e... [2] https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#repr-annotations-accepted-on-enums https://rust-lang.github.io/unsafe-code-guidelines/layout/en...
- drran 6y agoTo my surprise, it works partially on latest stable Rust: #[non_exhaustive] #[repr(C)] #[derive(Debug)] #[allow(dead_code)] enum Foo { A = 0, B = 1, C = 2, } pub fn main() { let bar = unsafe { std::mem::transmute::<i32, Foo>(33) }; println!("{:?}", bar); let s = match bar { Foo::A => ("A", bar as i32), Foo::B => ("B", bar as i32), Foo::C => ("C", bar as i32), _ => ("Unknown", bar as i32), }; println!("{:?}", s); } Standard Output C ("Unknown", 33)
- lilyball 6y agoIn what way is johnsoft’s code not forwards-compatible? > Your code is not forward compatible. The proper way is to implement it is as i32 and then unwrap it That’s what they’re doing. It’s a newtype for i32 with a few named values but it can represent any i32, and you can unwrap it with .0 to get the i32 value.