Appendix C: Reading Compiler Errors
Rust’s diagnostics are a feature of the language, not an afterthought. Each one
names the error code, states the problem in a sentence, marks the source spans
that caused it, and often writes the correction. rustc --explain E0382 prints
a page on any code.
Read them from the top and change one thing at a time. The errors below are the
ones you will meet in the first month. Each one has a program that provokes it
in examples/errors, named for the mistake, so you can produce any of these
messages yourself with rustc assign_twice.rs and then try the fix.
E0384: cannot assign twice to immutable variable
error[E0384]: cannot assign twice to immutable variable `fahr`
2 | let fahr = 0;
| ---- first assignment to `fahr`
3 | fahr = fahr + 20;
| ^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
help: consider making this binding mutable
2 | let mut fahr = 0;
You assigned to a let. Add mut, or, if the value is being refined rather
than changed, declare a new one with the same name and let it shadow.
E0596: cannot borrow as mutable
error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable
3 | v.push(2);
| ^ cannot borrow as mutable
help: consider changing this to be mutable
2 | let mut v = vec![1];
The same problem seen through a method. push needs &mut self and the
variable is not mut.
E0308: mismatched types
error[E0308]: mismatched types
1 | fn double(n: i32) -> i32 {
| ------ ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | n * 2;
| - help: remove this semicolon to return this value
The commonest form of this is a stray semicolon on the last line of a function,
which turns the value into nothing. The general form is that two types met and
did not agree, and the message says which was expected and which arrived. When
the two are numeric, the cause is Rust not converting for you: add as, or
i64::from(...), or pick matching types.
E0425: cannot find value
A name is not in scope. Check the spelling, check whether it needed a use
line, and check whether it was declared in a block that has already ended.
E0004: non-exhaustive patterns
error[E0004]: non-exhaustive patterns: `i32::MIN..=0_i32` and `3_i32..=i32::MAX` not covered
3 | let name = match n {
| ^ patterns not covered
A match left a case out. Either handle it or add _ => .... When this fires
on your own enum after you added a variant, that is the feature working: the
compiler is listing the places that now need attention.
E0277: trait bound not satisfied
error[E0277]: `Point` doesn't implement `Debug`
7 | println!("{p:?}");
| ^^^^^ `Point` cannot be formatted using `{:?}` because it doesn't implement `Debug`
= help: the trait `Debug` is not implemented for `Point`
help: consider annotating `Point` with `#[derive(Debug)]`
Something was asked to do a thing its type cannot do. Printing a struct that
does not derive Debug is the usual case, and the help line is the fix. In
generic code, the same error means a missing bound on the type parameter. Note
that the message also appears for mixed arithmetic: cannot multiply {integer} by {float} is this error wearing different clothes, because * is a trait.
E0382: use of moved value
error[E0382]: use of moved value: `h.name`
--> moved_field.rs:12:18
|
11 | let n = take(h.name);
| ------ value moved here
12 | let m = take(h.name);
| ^^^^^^ value used here after move
|
= note: move occurs because `h.name` has type `String`, which does not implement the `Copy` trait
A value was handed away and then used again. Four fixes, in the order to
consider them. Pass a reference instead, if the function only needs to read.
Have the function hand the value back. Restructure so the value is used once.
Or .clone() it, which is correct and costs a copy.
E0507: cannot move out of borrowed content
error[E0507]: cannot move out of `r.name` which is behind a shared reference
--> move_out_of_ref.rs:8:25
|
8 | let taken: String = r.name;
| ^^^^^^ move occurs because `r.name` has type `String`, which does not implement the `Copy` trait
|
help: consider borrowing here
|
8 | let taken: String = &r.name;
| +
help: consider cloning the value if the performance cost is acceptable
|
8 | let taken: String = r.name.clone();
| ++++++++
You have a borrow and tried to take ownership of something inside it. You cannot empty out something you do not own. Borrow the field instead, or clone it, or take the whole structure by value if that is what you meant.
E0502: cannot borrow as mutable because also borrowed as immutable
error[E0502]: cannot borrow `names` as mutable because it is also borrowed as immutable
3 | let first = &names[0];
| ----- immutable borrow occurs here
4 | names.push(String::from("grace"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
5 | println!("{first}");
| ----- immutable borrow later used here
Something is being modified while something else is looking at it. The third span is the one to attend to: the borrow lives until its last use, so moving that use earlier, or ending it before the modification, often fixes the program as it stands. If the loop genuinely needs to modify what it is walking, collect the changes and apply them afterwards.
E0499: cannot borrow as mutable more than once
error[E0499]: cannot borrow `v` as mutable more than once at a time
3 | let a = &mut v;
| ------ first mutable borrow occurs here
4 | let b = &mut v;
| ^^^^^^ second mutable borrow occurs here
One mutable borrow at a time. Usually the second one is not needed; use the
first. When two parts of a structure really are needed at once, take them from
different fields, or use split_at_mut on a slice.
E0106: missing lifetime specifier
error[E0106]: missing lifetime specifier
1 | fn longest(a: &str, b: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
= help: this function's return type contains a borrowed value, but the signature
does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
1 | fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
The function returns a reference and the compiler cannot tell what it borrows from. Take the suggested annotation. When the message instead says there is no value for the result to be borrowed from, you are returning a reference to a local, and the fix is to return an owned value.
E0597: does not live long enough
error[E0597]: `inner` does not live long enough
4 | let inner = String::from("hello");
| ----- binding `inner` declared here
5 | outer = &inner;
| ^^^^^^ borrowed value does not live long enough
6 | }
| - `inner` dropped here while still borrowed
7 | println!("{outer}");
| ----- borrow later used here
A reference outlived what it pointed at. The four spans give the sequence: where the value was made, where it was borrowed, where it died, and where the dead reference was used. Either move the value out to the longer scope or store an owned copy rather than a reference.
Warnings
Warnings are worth the same attention as errors. An unused variable often means
a line that was supposed to use it. An unused Result means an error being
dropped on the floor. An unused mut means the mutation you thought you were
doing is not happening. cargo clippy adds a few hundred more, and early on it
is the cheapest teacher available.