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¶
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.
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:
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.
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.
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¶
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¶
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¶
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); }
- Read all seven before changing anything. Group them by cause, not by line.
- Change
name: strtoname: Stringand recompile. How many errors went away? Why that many? - Resolve the
Defaultconflict. Which of the two impls should survive, and what does the other one know that it doesn't? - 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¶
- Debug and Display — the two
E0277s above, argued properly - What a struct is · STRUCTS.md
- A name is not a place — why
E0594points at the binding - What a warning is asking — the same habit, applied to the messages that do not stop the build
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
Displayprzy{}— tę cechę trzeba napisać ręcznie, kompilator nie zgadnie, jak człowiek ma czytać twój typ; - brak
Debugprzy{:?}— ten sam kod, odwrotna rada: tu wystarczy#[derive(Debug)]irustcsam to proponuje; - pole typu
str— wbrew kodowi to wcale nie brak implementacji, tylko brak rozmiaru:strto sam tekst o nieznanej długości, więc polem może być&str(wycinek łańcucha),String(łańcuch znaków na stercie) alboBox<str>; pole o rozmiarze nieznanym w czasie kompilacji wolno mieć wyłącznie na ostatniej pozycji; EqbezPartialEq—Eqnie dodaje żadnej metody, jest tylko obietnicą oPartialEq(żea == azawsze zachodzi), więc obie idą zawsze parą. Rozdzielają je liczby zmiennoprzecinkowe:f64maPartialEq, ale nieEq, boNaN != 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