RFA-558 · Case file with fixtures · Case 530 of 694 · Compiler evidence
Rust repr Accepts Layout Hints, Not Domain Format Names
repr controls selected Rust layout properties using a fixed language vocabulary. Wire encoding, byte order, and protocol validation need explicit serialization logic.
- 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
- The builtin repr attribute has a closed language vocabulary for selected memory-layout guarantees, not domain-specific encoding names.
- First discriminating check
- Use only a supported hint for its documented layout promise and implement byte order, framing, and validation through explicit serialization.
#[repr(...)] is not an open vocabulary for describing a domain format. Rust recognises a defined set of layout hints. On Rust 1.98.1, writing a protocol word such as network produces E0539.
The failing fixture annotates Header with repr(network). Rust has no representation mode by that name.
repr changes selected in-memory layout rules
Supported hints include Rust, C, transparent, integer representations for appropriate enums, alignment, and packing forms under their item restrictions. Each changes specific layout guarantees.
The Reference's type representation section is the authoritative list and describes how hints combine. The official E0539 page documents malformed repr input.
A representation attribute is a language-level layout contract, not a custom marker consumed by application code.
Network byte order is serialization, not repr
Network protocols usually specify byte order, field widths, framing, and validity. In-memory u16 values use the target's native representation. repr(C) does not convert integers to big endian.
For an explicit two-byte field I use operations such as u16::to_be_bytes and from_be_bytes, or a reviewed serialization library matching the protocol.
This produces a byte array with defined order regardless of CPU endianness. It also gives a place to validate lengths, reserved bits, and version fields.
The repaired fixture demonstrates a recognised hint only
The repaired fixture uses repr(C), which is legal on a struct. Its size assertion is deliberately modest.
I would not use that struct itself as a network packet by casting pointers to bytes. Padding, alignment, endianness, valid bit patterns, and protocol evolution still need consideration. Legal repr syntax is only the first condition.
The safe production repair for a network header is commonly an encode/decode function, not merely replacing network with C.
Repr C is mainly an ABI layout contract
repr(C) asks Rust to lay out a struct or enum according to specified C-compatible rules for supported forms. It is useful at FFI boundaries when paired with C-compatible field types and matching declarations.
It does not make types containing String, references, Rust enums, or trait objects automatically safe to pass through C. Ownership, validity, unwinding, and calling conventions remain separate contracts.
I test FFI layouts on supported targets and avoid assuming that one size assertion proves full ABI compatibility.
Transparent wrappers have a narrower promise
repr(transparent) is useful for a single-field wrapper that needs the layout and ABI of its non-zero-sized field under documented conditions. It can preserve domain type safety without inventing a new wire representation.
It still does not perform serialization or validate foreign values. A transparent newtype around u16 has native-endian memory until code explicitly encodes it.
Custom metadata needs its own attribute namespace
Procedural macros and tools can define or consume helper attributes under their supported syntax. If network is domain metadata, it belongs to such a tool or to ordinary types and functions, not inside builtin repr.
I avoid attributes that only look declarative while hiding important fallible conversion. Protocol code benefits from explicit bytes-in/typed-value-out boundaries.
Round-trip tests should use fixed byte fixtures
For a wire header, I keep known byte sequences from the protocol specification and test decoding followed by encoding. I include boundary values, truncated input, unknown flags, and the opposite host-endian concern explicitly. size_of tests are useful for ABI structures but cannot replace these byte-level cases. This distinction lets the same domain type use a convenient Rust memory layout internally while its encoder maintains a stable external representation across compiler versions and target architectures.
My E0539 checklist
- Is the hint in Rust's fixed supported repr list?
- Is it valid for this kind of item?
- Am I trying to express byte order or a wire format through memory layout?
- Should encoding use explicit
to_be_bytesor a protocol library? - Are padding, alignment, and invalid bit patterns handled?
- Does an FFI boundary use only compatible field types?
- Would
repr(transparent)fit a domain newtype's actual promise? - Is custom metadata better handled by a documented macro attribute?
The core principle is that memory layout and data encoding are different layers. I use repr only for its documented layout guarantees and make protocol bytes explicit.