RFA-561 · Case file with fixtures · Case 533 of 694 · Compiler evidence
A Rust Enum Cannot Have Two Conflicting Integer Representations
Representation hints are layout contracts, not fallback lists. Select one stable representation or make target-conditional choices mutually exclusive.
- Reviewed
- Rust
- Rust 1.98.1, edition 2024
- Targets
- all Rust targets
- Profiles
- dev, release, test
Direct answer
What this Rust failure means
- Why it happens
- Representation hints are concrete layout requirements rather than ordered fallbacks, and these two integer widths cannot both describe one discriminant.
- First discriminating check
- Select the one ABI width actually required or make target conditions exhaustive and mutually exclusive, then validate external discriminants safely.
Representation hints do not mean “try these layouts in order.” Each one adds a concrete layout requirement. Two incompatible integer representations on the same enum produce E0566 because the compiler cannot make the discriminant both widths.
The failing fixture declares #[repr(u16, u32)] on Status.
One item needs one coherent layout contract
An integer representation on an enum controls documented aspects of its discriminant representation. u16 and u32 make different size and alignment commitments, so they cannot both apply.
The official E0566 explanation recommends choosing one hint. The type layout Reference describes which repr forms are valid and how compatible hints may combine.
I treat every repr attribute as part of an unsafe, FFI, or persistence review rather than visual decoration.
The repaired fixture selects u16 deliberately
The repaired fixture keeps repr(u16) and verifies the explicit discriminant values through casts.
The choice is plausible because values one and two fit, but fitting current variants is not the only question. A public FFI contract must reserve enough space for future values, match the foreign declaration, and define how unknown values are handled.
Changing representation later can break ABI or persisted data even when Rust source recompiles.
Conditional representation must be mutually exclusive
If different targets genuinely need different hints, cfg_attr can apply one attribute under one condition and another under its complement. The Reference documents cfg_attr.
I verify the conditions are exhaustive and non-overlapping. Two features may both be enabled unless declared mutually exclusive, recreating E0566 only in one CI combination.
Target-specific layout is also dangerous for bytes stored on disk or sent over a network. Those formats should usually use explicit serialization independent of native layout.
Repr C and integer repr may combine under specific rules
Not every list of two hints is conflicting. Rust documents combinations such as C representation with primitive enum representation for appropriate enum forms. Their effect is defined, not guessed.
I consult the current Reference rather than infer compatibility from the absence of E0566. A combination that compiles may still create padding or a tagged-union layout different from a hand-written foreign type.
Tests should check offsets and layouts through suitable tooling on every supported target, not only size_of on one laptop.
Representation does not validate foreign discriminants
Even with repr(u16), not every u16 bit pattern is necessarily a valid Status. Constructing an enum from arbitrary foreign bytes through unchecked transmute can be undefined behaviour.
I parse the integer, match known values, and return an error or explicit unknown variant representation. The repr decides layout; conversion decides validity.
Avoid native layout as a durable storage schema
Compiler and target layout rules may include padding, endianness, and other properties unsuitable for a stable file format. An enum stored as bytes should have a versioned encoder and decoder.
The explicit discriminants in the fixture can inform that encoder without requiring the entire in-memory enum layout to equal the wire format.
Header generation should have one representation owner
Conflicts often appear after a macro, bindgen step, or conditional attribute adds a second hint to a hand-written enum. I inspect expanded attributes and decide whether Rust source, a foreign header, or the generator configuration owns the representation. Then I remove the duplicate at that source rather than patching generated output. A compile fixture for each feature combination and a C-side static assertion where appropriate provide stronger evidence than reviewing only the final Rust annotation. One owner and one generated contract keep layout changes visible.
My E0566 checklist
- Which repr hints are attached to the same item after macro expansion?
- Do two integer hints make incompatible promises?
- What one width does the actual ABI or domain require?
- Could feature or target conditions overlap?
- Is a compiled hint combination explicitly documented as valid?
- Are unknown foreign discriminants validated before enum construction?
- Does persisted or network data use explicit encoding?
- Are supported targets covered by layout and integration tests?
The core principle is that repr is a concrete layout contract, not a preference list. I select one coherent promise and keep conditional choices provably exclusive.