Skip to content

When a struct refuses

Level: 101 → 201 · working knowledge

One line: Eight refusals you will meet in your first week of writing structs — and the useful surprise is that four of them are the same error code meaning four unrelated things, so the code is never the diagnosis.


Read the note:, not the first line

Every error below prints its own fix. Seven of the eight include the exact edit; the eighth names both conflicting sites. What people do instead is read the red line, recognise the code, and guess — which works right up until E0277 shows up, because E0277 is just "a trait bound was not satisfied" and for structs that covers at least four different mistakes.

Code Reads as Actually about
E0063 missing field there is no partly-built struct
E0277 trait bound not satisfied no Display — you have to write it
E0277 trait bound not satisfied no Debug — but this one it will generate
E0277 trait bound not satisfied a str field — a missing size, not a missing impl
E0277 trait bound not satisfied Eq without PartialEq
E0119 conflicting implementations you both derived and hand-wrote a trait
E0594 not declared as mutable mut belongs to the binding, and you made a new one
E0282 type annotations needed a function in impl<T> that never mentions T

E0063 — there is no partly-built struct

Real rustc output — rustc 1.98.0, edition 2024
error[E0063]: missing field `name` in initializer of `Player`
 --> e1.rs:2:22
  |
2 | fn main() { let _b = Player { score: 5 }; }
  |                      ^^^^^^ missing `name`

Rust has no uninitialized state for a struct — the moment one exists, every field has a value. So name them all, or hand the job to something that can: ..Default::default() fills the rest. Watch what that gives you, though: a derived Default is the type's zero, not your domain's, so a missing name becomes "" rather than an error.

E0277, four ways

No Display, from {}. Rust will not guess how a human should read your type.

Abridged — real rustc 1.98.0 output, without the --> headers
error[E0277]: `Player` doesn't implement `std::fmt::Display`
  |                       --   ^ `Player` cannot be formatted with the default formatter
help: the trait `std::fmt::Display` is not implemented for `Player`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead

No Debug, from {:?}. Same code, opposite advice — this one it offers to generate:

Abridged — real rustc 1.98.0 output, without the --> headers
error[E0277]: `Player` doesn't implement `Debug`
  |                       ----   ^ `Player` cannot be formatted using `{:?}` because it doesn't implement `Debug`
  = help: the trait `Debug` is not implemented for `Player`
help: consider annotating `Player` with `#[derive(Debug)]`
  |
1 + #[derive(Debug)]

Which of the two you want, and why the language refuses to guess the first, is Debug and Display.

A str field. This one is misfiled by the code: it is not a missing impl at all, it is a missing size.

Abridged — real rustc 1.98.0 output, without the --> headers
error[E0277]: the size for values of type `str` cannot be known at compilation time
  |                       ^^^ doesn't have a size known at compile-time
  = help: the trait `Sized` is not implemented for `str`
  = note: only the last field of a struct may have a dynamically sized type
  = help: change the field's type to have a statically known size
help: borrowed types always have a statically known size
  | struct Player { name: &str, score: u8 }
help: the `Box` type always has a statically known size and allocates its contents in the heap
  | struct Player { name: Box<str>, score: u8 }

str is the text itself, of unknown length; &str is a reference to it and String/Box<str> own it on the heap. All three of those have a size. The note: is the part worth keeping: a dynamically sized field is legal, but only in last position.

#[derive(Eq)] on its own.

Abridged — real rustc 1.98.0 output, without the --> headers
error[E0277]: can't compare `Player` with `Player`
  2 | struct Player { name: String, score: u8 }
    |        ^^^^^^ no implementation for `Player == Player`
    = help: the trait `PartialEq` is not implemented for `Player`
note: required by a bound in `Eq`
help: consider annotating `Player` with `#[derive(PartialEq)]`
    |
  2 + #[derive(PartialEq)]

Eq adds no methods. It is a promise about PartialEq — that == is reflexive, so a == a always holds. That promise needs the == it is about, which is why the pair is always derived together. (Floats are the reason the split exists: f64 has PartialEq and not Eq, because NaN != NaN.)

E0119 — derived and hand-written are two impls

Abridged — real rustc 1.98.0 output, without the --> header
error[E0119]: conflicting implementations of trait `Default` for type `Player`
1 | #[derive(Default)]
  |          ^^^^^^^ conflicting implementation for `Player`
3 | impl Default for Player { … }
  | ----------------------- first implementation here

A derive writes an impl. Two impls of one trait for one type is the coherence rule saying no. Keep whichever knows more — usually the hand-written one, since a derived Default can only ever produce the type's zero, while yours can encode a real default like a retry limit of ten.

E0594 — mut belongs to the binding

Abridged — real rustc 1.98.0 output, without the --> header
error[E0594]: cannot assign to `b.score`, as `b` is not declared as mutable
6 |     b.score = 6;
  |     ^^^^^^^^^^^ cannot assign
help: consider changing this to be mutable
5 |     let mut b = b;
  |         +++

Note where the help: points — line 5, not line 6. The assignment is fine; the rebinding on the line before dropped the mut. let b = b; is a deliberate idiom for freezing a value after its setup phase, and this error is that idiom working. There is no such thing as a mutable field: a name is not a place.

E0282 — nothing to infer T from

Abridged — real rustc 1.98.0 output, without the --> header
error[E0282]: type annotations needed
4 |     fn check() { let _q = Stack::capacity(); }
  |                           ^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Stack`
help: consider specifying a concrete type for the type parameter `T`
4 |     fn check() { let _q = Stack::</* Type */>::capacity(); }
  |                                ++++++++++++++

capacity() takes no T, returns no T, and mentions no T — but it lives inside impl<T> Stack<T>, so calling it still requires choosing one, and there is nothing in the call to choose from. Three fixes, in order of preference: give it a receiver (self.capacity()), name the type (Stack::<i32>::capacity()), or — usually the right answer — move it out of the generic impl, because a function with no T in it never belonged there.


Practice

Seven errors, five root causes, three edits. This arrives from a colleague and refuses seven times:

#[derive(Debug, Default, Eq)]
struct Player { name: str, level: u8 }
impl Default for Player { fn default() -> Self { unimplemented!() } }
fn main() { let v = Player { level: 1 }; println!("{}", v); }
  1. Read all seven before changing anything. Group them by cause, not by line.
  2. Change name: str to name: String and recompile. How many errors went away? Why that many?
  3. Resolve the Default conflict. Which of the two impls should survive, and what does the other one know that it doesn't?
  4. Fix the last two. Which of them could rustc not have written for you, and why not?
Solution

when_a_struct_refuses_kata.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

//! Kata solution: seven errors, five root causes, three edits.
//!
//!   rustc --edition 2024 when_a_struct_refuses_kata.rs -o /tmp/wsrk && /tmp/wsrk

use std::fmt;

// The struct as it arrives is four lines and refuses seven times:
//
//     #[derive(Debug, Default, Eq)]
//     struct Player { name: str, level: u8 }
//     impl Default for Player { fn default() -> Self { unimplemented!() } }
//     fn main() { let v = Player { level: 1 }; println!("{}", v); }
//
// Fixed, with a note on each edit:

#[derive(Debug, PartialEq, Eq)] // edit 2: Default dropped (it clashes), PartialEq added
struct Player {
    name: String, // edit 1: str -> String
    level: u8,
}

impl Default for Player {
    fn default() -> Self {
        Player { name: String::from("anonymous"), level: 0 }
    }
}

impl fmt::Display for Player {
    // edit 3: `{}` needs this written by hand
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "level {} — {}", self.level, self.name)
    }
}

fn main() {
    println!("The error COUNT is not the work count.\n");

    println!("  7 errors   as it arrives");
    println!("  4 errors   after edit 1: `name: str` -> `name: String`");
    println!("  2 errors   after edit 2: drop derived Default, add PartialEq");
    println!("  0 errors   after edit 3: impl Display, and name every field\n");

    println!("Edit 1 removed THREE of the seven, because one root cause produced them:");
    println!("  E0277  str doesn't have a size known at compile-time   (x2)");
    println!("  E0277  the trait bound `str: Default` is not satisfied");
    println!("An unsized field poisons every derive that has to touch it. Chasing the");
    println!("three separately would have been three investigations of one mistake.\n");

    println!("Edit 2 removed two more, and they were unrelated to each other:");
    println!("  E0119  conflicting implementations of `Default`  — derive AND impl");
    println!("  E0277  can't compare `Player` with `Player`        — Eq without PartialEq");
    println!("Keeping the hand-written Default is the right call: it knows a domain");
    println!("default the derive could never guess.");
    println!("  Player::default() = {}", Player::default());
    println!("  ...where the derive would have said name: \"\", level: 0\n");

    println!("Edit 3 was the only one rustc could not write for you:");
    println!("  E0063  missing field `name`   — it named the field");
    println!("  E0277  doesn't implement Display — it suggested {{:?}} instead");
    println!("The suggestion is a real option, and often the right one. Choosing to");
    println!("write Display means you decided a human reads this type.");
    let v = Player { name: String::from("Ada"), level: 7 };
    println!("  Display: {v}");
    println!("  Debug:   {v:?}");
    println!("  and the pair now compares: {}", v == Player { name: "Ada".into(), level: 7 });

    println!("\nThe habit: read all seven before editing any of them, and group them by");
    println!("root cause. rustc reports every error it can reach in one pass — it is not");
    println!("a queue to be worked one at a time.");
}

Verified output of when_a_struct_refuses_kata.rs — regenerated by tools/run_examples.py, never hand-typed.

The error COUNT is not the work count.

  7 errors   as it arrives
  4 errors   after edit 1: `name: str` -> `name: String`
  2 errors   after edit 2: drop derived Default, add PartialEq
  0 errors   after edit 3: impl Display, and name every field

Edit 1 removed THREE of the seven, because one root cause produced them:
  E0277  str doesn't have a size known at compile-time   (x2)
  E0277  the trait bound `str: Default` is not satisfied
An unsized field poisons every derive that has to touch it. Chasing the
three separately would have been three investigations of one mistake.

Edit 2 removed two more, and they were unrelated to each other:
  E0119  conflicting implementations of `Default`  — derive AND impl
  E0277  can't compare `Player` with `Player`        — Eq without PartialEq
Keeping the hand-written Default is the right call: it knows a domain
default the derive could never guess.
  Player::default() = level 0 — anonymous
  ...where the derive would have said name: "", level: 0

Edit 3 was the only one rustc could not write for you:
  E0063  missing field `name`   — it named the field
  E0277  doesn't implement Display — it suggested {:?} instead
The suggestion is a real option, and often the right one. Choosing to
write Display means you decided a human reads this type.
  Display: level 7 — Ada
  Debug:   Player { name: "Ada", level: 7 }
  and the pair now compares: true

The habit: read all seven before editing any of them, and group them by
root cause. rustc reports every error it can reach in one pass — it is not
a queue to be worked one at a time.

The verified output

Verified output of when_a_struct_refuses.rs — regenerated by tools/run_examples.py, never hand-typed.

1. E0063 — missing field in initializer
   There is no partly-built struct in Rust. Name every field, or let
   something else supply the rest:
   named all:            Player { name: "Ada", score: 5 }   (name "Ada", score 5)
   ..Default::default(): Player { name: "", score: 5 }   <- and this needs Default to exist
   note the derived Default gave name an EMPTY string, not a missing one:
   the type's zero, which is rarely your domain's. b.name.is_empty() = true

2. E0277 — one code, four different problems
   E0277 is 'a trait bound was not satisfied'. For structs it shows up as:

   2a. no Display, from `{}`
       Rust will not guess how you want a human to read your type.
       Ada scored 5

   2b. no Debug, from `{:?}`
       Same code, opposite advice: this one it WILL generate.
       Level(3)

   2c. `str` as a field type — not a missing impl, a missing SIZE
       str is unsized, so it cannot be a field. Borrow it or box it:
       &str  field: Ada   Box<str> field: Ada
       The note is the part to read: 'only the LAST field of a struct
       may have a dynamically sized type'.

   2d. `#[derive(Eq)]` alone — Eq is a promise about PartialEq
       Eq adds no methods. It says == is reflexive, so it needs the
       PartialEq that defines ==. Always derive the pair:
       Level(3) == Level(3) is true

3. E0119 — conflicting implementations
   #[derive(Default)] AND `impl Default` is two impls of one trait.
   Keep the one that knows something the other cannot:
   derived would be Retries { limit: 0 }; ours says limit = 10

4. E0594 — cannot assign, not declared as mutable
   `let b = b;` rebinds. mut belongs to the BINDING, so the new one
   does not inherit it — and rustc's help points at line 5, not line 6.
   The value never changed; the name did.

5. E0282 — type annotations needed
   `Stack::capacity()` names no T anywhere, but it lives in impl<T>, so
   there is nothing to infer T from. Three fixes, in order of preference:
     Stack::<i32>::capacity() = 10   name it at the call
     t.rows()                 = 3   or have a receiver to infer from
     ...or move it out of impl<T> entirely, if it truly has no T in it.

The pattern across all eight: rustc is not withholding a fix.
Seven of these print the exact edit, and the eighth (E0119) names both
conflicting sites. The skill is reading the `note:` line, not the first one.

See also

Po polsku

Ta strona uczy jednej rzeczy, którą łatwo przeoczyć: kod błędu nie jest diagnozą. E0277 mówi tylko tyle, że nie spełniono ograniczenia cechy (trait bound not satisfied), a przy strukturach stoją za tym cztery zupełnie różne pomyłki. Polski czytelnik ma tu dodatkową pokusę: komunikat rustc jest długi i po angielsku, więc szybciej wydaje się wpisać sam kod w wyszukiwarkę, niż przeczytać całość. Akurat przy E0277 ten skrót zawodzi — wyszukiwarka odpowie na cudzy problem. Odpowiedź jest zawsze niżej, w wierszach note: i help:, i w siedmiu przypadkach z ośmiu rustc podaje w nich gotową poprawkę.

Cztery odczytania E0277, które ta strona rozdziela:

  • brak Display przy {} — tę cechę trzeba napisać ręcznie, kompilator nie zgadnie, jak człowiek ma czytać twój typ;
  • brak Debug przy {:?} — ten sam kod, odwrotna rada: tu wystarczy #[derive(Debug)] i rustc sam to proponuje;
  • pole typu str — wbrew kodowi to wcale nie brak implementacji, tylko brak rozmiaru: str to sam tekst o nieznanej długości, więc polem może być &str (wycinek łańcucha), String (łańcuch znaków na stercie) albo Box<str>; pole o rozmiarze nieznanym w czasie kompilacji wolno mieć wyłącznie na ostatniej pozycji;
  • Eq bez PartialEqEq nie dodaje żadnej metody, jest tylko obietnicą o PartialEq (że a == a zawsze zachodzi), więc obie idą zawsze parą. Rozdzielają je liczby zmiennoprzecinkowe: f64 ma PartialEq, ale nie Eq, bo NaN != NaN.

Reszta tabelki mówi coś o tym, jak Rust jest zbudowany. E0063 — nie istnieje częściowo zbudowana struktura: w chwili, gdy powstaje, każde pole ma już wartość (..Default::default() dopełni resztę, ale zerami typu, a nie wartościami twojej dziedziny). E0119#[derive(...)] pisze impl, więc razem z ręcznym impl masz dwie implementacje jednej cechy dla jednego typu, czego reguła spójności zabrania; zostaw tę, która wie więcej, bo Default z derive potrafi dać tylko zero typu, a twój własny — prawdziwą wartość domyślną. E0594 mówi z kolei rzecz, którą polszczyzna wręcz podpowiada źle: nie ma czegoś takiego jak „pole mutowalne”. mut należy do wiązania nazwy, więc let b = b; tworzy nowe wiązanie już bez mut — i dlatego help: wskazuje wiersz z let, a nie wiersz z przypisaniem. A E0282 łapie metodę w impl<T>, która o T nigdzie nie wspomina: nie ma z czego wywnioskować typu, więc najlepszym wyjściem jest zwykle wyniesienie jej poza generyczny impl.

Na koniec arytmetyka z ćwiczenia, bo to najtrwalszy nawyk z całej strony: siedem błędów, pięć przyczyn, trzy poprawki. Jedno źle dobrane pole (name: str) wygenerowało od razu trzy komunikaty, bo psuje każdy derive, który musi go dotknąć. rustc zgłasza w jednym przebiegu wszystko, co jest w stanie zobaczyć — to nie jest kolejka do obsługiwania po jednym. Przeczytaj wszystkie, pogrupuj po przyczynach, dopiero potem edytuj.

Szukaj po polsku: struktury w Ruscie · kody błędów rustc · rust E0277 trait bound is not satisfied · rust str doesn't have a size known at compile-time · rust E0119 conflicting implementations