Mehdi Akiki
Rust Failure Atlas / Runtime, memory, and library APIs

RFA-439 · Case file with fixtures · Case 411 of 694 · Compiler evidence

Sized Is Implemented by the Rust Compiler, Not User Code

Sized records whether a type's size is known at compile time, and rustc derives that fact from representation. Do not implement it; rely on implicit bounds, use ?Sized to accept DSTs, and place unsized values behind suitable pointers.

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
Known size is a representation fact used by code generation, so user code cannot assert it independently of the compiler's layout analysis.
First discriminating check
Remove the impl, use implicit Sized bounds normally, or write T: ?Sized behind pointer indirection when dynamically sized referents must be accepted.

I knew my record had a fixed layout and tried to state that fact with impl Sized for Record. Rust rejected it with E0322 because users cannot implement Sized explicitly.

The failing fixture defines a tuple struct containing u32. It is already Sized by construction. The extra impl is both unnecessary and forbidden.

Sized is a compiler-known layout fact

The Sized trait indicates that a type's size is known at compile time. Rust's compiler determines this from the type's definition and built-in layout rules.

The E0322 explanation says all implementations are supplied automatically. User code cannot assert the property independently of representation.

This is important because the compiler uses size for stack layout, moves, calling conventions, and generic code generation. A mistaken manual impl could not make an actually unsized type have a fixed size.

The repair is to remove the impl

The repaired fixture deletes the explicit implementation and passes Record to a function requiring T: Sized. The bound is satisfied automatically.

For most generic type parameters, Sized is also an implicit default. These two declarations are normally equivalent:

fn store<T>(value: T) {}
fn store_explicit<T: Sized>(value: T) {}

Writing the explicit bound may be useful for teaching or contrast, but it usually adds no capability.

?Sized relaxes the implicit bound

When a generic should accept dynamically sized types behind references or pointers, Rust uses the special ?Sized relaxation:

fn inspect<T: ?Sized>(value: &T) {}

The ? form does not mean “T is unsized.” It means T may be either Sized or unsized. Because a by-value unsized parameter still lacks a known layout, the value is passed behind &T here.

Only ?Sized has this special relaxing role; arbitrary ?Trait syntax is not a general optional-bound feature.

Slices and trait objects are common DSTs

[T] and str are dynamically sized. A slice reference &[T] and string slice reference &str are themselves sized pointer values carrying metadata.

dyn Trait is also unsized and normally appears behind &, Box, Arc, or another pointer. The Atlas cases on wide pointers show that metadata is part of these pointer representations.

I distinguish the size of the pointer from the size of its referent. size_of_val can use metadata to report a dynamic referent size at runtime without making the type Sized.

Struct fields have an unsized-tail rule

A custom dynamically sized struct can place an unsized field last. Such values still need pointer indirection in ordinary use, and their metadata follows the tail.

Adding impl Sized cannot override that layout. Making the tail a Box<T> or another sized owner changes the structure itself and can make the outer type Sized.

I change representation when I need a sized value; I do not try to change the compiler's conclusion through a marker.

Sized affects trait methods and dyn compatibility

A trait has an implicitly potentially unsized Self, but adding Self: Sized as a supertrait excludes trait objects. Individual methods can use where Self: Sized to remain available only for concrete sized implementors while other methods stay dispatchable.

This is a capability boundary, not a performance annotation. A method returning Self by value, for example, naturally needs to know its size at the call site.

The Reference on special traits documents implicit Sized bounds and their relaxation.

References do not transfer the referent's sizedness

Both &u32 and &str are sized reference values even though u32 is Sized and str is not. Generic diagnostics can become confusing when a bound applies to the pointer rather than its target. I spell helper parameters as T: ?Sized with &T when the referent may be dynamic, and I test one ordinary value plus one slice or trait object to prove the intended flexibility.

My E0322 checklist

  • Is the explicit impl simply redundant?
  • Is the type actually unsized because of a slice, str, trait object, or unsized tail?
  • Should the API accept both sized and unsized referents with T: ?Sized?
  • Is pointer indirection required for storage or return?
  • Does Self: Sized unintentionally prevent trait objects?
  • Can representation change solve the actual storage requirement?

The core principle is that Sized reports a representation property known by the compiler. It is not a promise user code can opt into. I remove explicit impls, relax implicit bounds only where pointers support DSTs, and redesign storage when a fixed-size outer value is required.