Skip to content

Should enums use explicit usize values? #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sffc opened this issue Jun 6, 2020 · 10 comments · Fixed by #157
Closed

Should enums use explicit usize values? #115

sffc opened this issue Jun 6, 2020 · 10 comments · Fixed by #157
Assignees
Labels
A-ffi Area: FFI, WebAssembly, Transpilation C-data-infra Component: provider, datagen, fallback, adapters T-bug Type: Bad behavior, security, privacy
Milestone

Comments

@sffc
Copy link
Member

sffc commented Jun 6, 2020

A pattern we use a lot in ICU4C is to be explicit with the number value associated with an enum entry. This helps with binary stability: if an enum value gets added, the numerical value for the old enum values remains the same.

For example:

pub enum Category {
    Undefined = 0,
    Decimal = 1,
    PrivateUse = 4096,
}

Is this considered good practice in Rust?

@zbraniecki
Copy link
Member

I have not seen that being practiced outside of C compatibility.

@Manishearth
Copy link
Member

This is not typically good practice. Rust has a versioning, build, and dependency story so the problems in C that require stable ABI are largely not present. They are only present for types that are exposed to C, and even then cbindgen can have your back.

@filmil
Copy link
Contributor

filmil commented Jun 6, 2020 via email

@zbraniecki
Copy link
Member

I think when working on a library version skew is a real problem to keep in
mind so being explicit with numeric values helps. 2 cents, F

I don't think I understand what version skew is.

@Manishearth
Copy link
Member

version skew isn't a problem for enums in Rust because Rust isn't header file based. You use cargo, and the compiler reads the internal enum values from the crate metadata. It's only a problem if exposing a C ABI, and it's also easy to expose that via a second set of enums that does have fixed discriminants.

@filmil
Copy link
Contributor

filmil commented Jun 8, 2020

I don't think I understand what version skew is.

Version skew, as in a mismatch when a new code version attempts to read data written by an old code version, or the other way around. I think it is harder to change enum values accidentally if they are explicitly numerated. Ideally we will never knowingly make an incompatible change; in practice, this happens sometimes. IMHO we should make that harder to do.

@Manishearth
Copy link
Member

@filmil rust does not let you read enum discriminant values unless they are explicitly numerated. So there is no backwards compatibility hazard here.

@sffc
Copy link
Member Author

sffc commented Jun 8, 2020

They are only present for types that are exposed to C, and even then cbindgen can have your back.

@Manishearth Can you elaborate on how cbindgen resolves version skew problems?

With ICU4C, clients want to be able to swap in DLLs (.so files on Linux) from different versions of ICU and have clients work the same without having to be recompiled. How is this not a problem in Rust as well? How do you swap in a new .rlib if the enum constants changed?

EDIT: Also, to be clear, I'm talking about when you pass an enum into a function, like let result = foo(Category::Decimal);.

@Manishearth
Copy link
Member

cbindgen doesn't quite solve version skew, however we can use cbindgen to define a C API that has fixed discriminants with conversion functions to the native enum variants.

Typically Rust libraries exposing a C API do this whenever exposing a C API would have an unidiomatic effect on the Rust API (e.g. adding explicit discriminants including the weird PrivateUse cap)

How is this not a problem in Rust as well? How do you swap in a new .rlib if the enum constants changed?

Rust doesn't have a stable ABI, you are not supposed to do this. Rust doesn't work this way. If you want to have swappable libraries you need to create a lib with a C API.

If you are compiling crate B that depends on crate A, you cannot swap in A1.rlib with A2.rlib without recompiling crate B.

@sffc sffc self-assigned this Jun 8, 2020
@sffc sffc added C-data-infra Component: provider, datagen, fallback, adapters T-bug Type: Bad behavior, security, privacy A-ffi Area: FFI, WebAssembly, Transpilation labels Jun 8, 2020
@sffc
Copy link
Member Author

sffc commented Jun 8, 2020

OK, thanks for the explanation. I'll open a PR to remove the fixed discriminants.

@sffc sffc added this to the 2020 Q2 milestone Jun 17, 2020
@sffc sffc closed this as completed in #157 Jun 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ffi Area: FFI, WebAssembly, Transpilation C-data-infra Component: provider, datagen, fallback, adapters T-bug Type: Bad behavior, security, privacy
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants