Mehdi Akiki
Rust Failure Atlas / FFI and targets

RFA-569 · Case file with fixtures · Case 541 of 694 · Compiler evidence

Rust repr(align) Values Must Be Supported Powers of Two

Alignment is a divisibility promise used by layout and pointer validity. Choose a legal power of two for a measured requirement and account for size padding.

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
Payload size was mistaken for an address divisibility requirement, but Rust represents explicit alignment through bounded powers of two.
First discriminating check
Choose a legal power of two only for a measured hardware or ABI need, then inspect padding, allocators, containing layouts, and target coverage.

Alignment is expressed as a power-of-two byte boundary in Rust's representation attribute. Requesting 24-byte alignment cannot be represented by this contract, so rustc emits E0589.

The failing fixture applies repr(align(24)) to a 24-byte payload.

Size and alignment are different properties

A type containing 24 bytes of data does not naturally need 24-byte alignment. Size says how much storage one value occupies. Alignment says which addresses are valid starts for values of that type.

The official E0589 page requires the explicit value to be a power of two and within Rust's supported maximum.

Choosing alignment from payload length is usually a conceptual mistake.

The repaired type aligns to 32 bytes

The repaired fixture uses repr(align(32)). It asserts both alignment and size.

The size becomes 32 because array elements of this type must each begin at a 32-byte boundary. Padding rounds the 24-byte field storage up to a multiple of the type's alignment.

The standard align_of function reports the ABI-required minimum alignment for the current target.

Larger alignment needs a measured reason

Explicit alignment may support SIMD instructions, cache-line separation, DMA requirements, or a foreign ABI. It can also waste memory and reduce cache density.

Calling a type CacheLine does not prove 32 is the machine's cache-line size or that alignment prevents false sharing. Hardware and target environments differ, and adjacent allocations may have other layout effects.

I benchmark the actual workload and document the supported targets. Representation attributes should follow evidence, not performance folklore.

Alignment affects every containing type

A highly aligned field raises its containing struct's alignment and may insert padding before or after other fields. Arrays and vectors use a stride compatible with element size and alignment.

This can substantially increase memory use in large collections. I inspect size_of, align_of, field offsets where required, and allocation APIs that must satisfy the alignment.

The Reference documents alignment modifiers, their valid range, and interactions with other repr hints.

Packed and align cannot be combined casually

Packing lowers alignment to reduce padding, while explicit align raises it. Rust restricts conflicting combinations and nested layouts. Packed fields can be unaligned, making references to them unsafe or invalid to create directly.

I do not use packed as the opposite-number fix for E0589. It serves a different low-level purpose and adds access hazards.

Foreign allocation must meet Rust alignment

If a pointer comes from C, shared memory, or a device buffer, declaring an aligned Rust type does not retroactively align the address. The allocator or foreign contract must provide a suitably aligned pointer before a reference is formed.

I validate raw pointer alignment and length at unsafe boundaries. A correct type declaration is one part of that proof.

Alignment optimisations need before-and-after evidence

For false-sharing or SIMD work, I record the target CPU, allocator, thread count, structure size, and benchmark variance. I compare throughput and tail latency while also measuring memory growth. An alignment change can help one hot array and hurt another workload through larger strides or cache pressure. Keeping these measurements beside the code prevents a legal align(32) from becoming permanent folklore after the original hardware disappears.

Public aligned types affect downstream allocation

Increasing alignment in a public type can change its layout and every container holding it. FFI callers and custom allocators may no longer meet the requirement. I treat that as a compatibility change, review zero-copy casts, and test arrays as well as individual values. Correctness comes before the performance motivation.

My E0589 checklist

  • Is the requested alignment a supported power of two?
  • Did I confuse payload size with address alignment?
  • What hardware, ABI, or measured performance requirement needs it?
  • How much padding does it add to the type and containing collections?
  • Are every allocator and foreign pointer source aligned enough?
  • Does the type interact with packed representation?
  • Are layout checks run on all supported targets?
  • Does a benchmark justify the memory tradeoff?

The core principle is that alignment is a low-level address promise. I use a legal power of two only when an ABI or measured systems requirement makes that promise necessary.