Why this matters
You will see more compiler errors while learning Rust than in almost any other language. That is not a sign you are doing badly; it is how Rust works. The compiler checks things other languages leave to run time (or never check at all), and it explains itself in detail. Programmers who get good at Rust are not the ones who make fewer errors at first, they are the ones who read the messages properly instead of guessing. This lesson takes a message apart so you know where to look.
Anatomy of an error
Here is a program with a typo, and what rustc says about it:
fn main() {
let count = 3;
println!("{}", cont);
}
error[E0425]: cannot find value `cont` in this scope
--> main.rs:3:20
|
3 | println!("{}", cont);
| ^^^^ help: a local variable with a similar name exists: `count`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0425`.
Line by line:
error[E0425]: cannot find value `cont` in this scope <- severity, code, headline
--> main.rs:3:20 <- file:line:column
|
3 | println!("{}", cont); <- the source line
| ^^^^ help: ... <- span and label
- Severity:
error(the build fails) orwarning(it does not). - Code
E0425: a stable identifier for this kind of error. Not every error has one; syntax errors usually do not. - Headline: one sentence saying what is wrong. Read it slowly. It is precise: "cannot find value
cont" means exactly that the namecontdoes not refer to anything here. - Location:
main.rs:3:20is line 3, column 20. Editors jump there. - Span: the
^^^^underline marks the exact piece of code the message is about. - Labels, notes and help: extra lines attached to spans.
help:usually contains a concrete fix.note:gives background.
Some messages point at several places. - marks secondary spans (context), ^ the primary one. You saw this already with the missing semicolon: one span said where the ; belongs, another marked the token that surprised the parser.
Looking up an error code
Every error code has a long explanation with examples, built into the compiler:
$ rustc --explain E0308
Expected type did not match the received type.
Erroneous code examples:
fn plus_one(x: i32) -> i32 {
x + 1
}
plus_one("Not a number");
// ^^^^^^^^^^^^^^ expected `i32`, found `&str`
...
The same text is online in the Rust error codes index. When an error confuses you, run --explain before anything else: it describes the rule being enforced, which is usually what you are missing.
A tour of common errors
Type mismatch (E0308). Types are checked everywhere. Here a variable is declared as an integer (i32) and given text:
fn main() {
let x: i32 = "five";
}
error[E0308]: mismatched types
--> main.rs:2:18
|
2 | let x: i32 = "five";
| --- ^^^^^^ expected `i32`, found `&str`
| |
| expected due to this
Two spans: the primary one on the value, and a secondary one explaining why the compiler expected i32 (because you wrote it). &str is the type of a string literal; you will meet it properly in module 6.
Operation not defined (E0277). Rust does not convert between numbers and text for you:
fn main() {
let total = 2 + "3";
}
error[E0277]: cannot add `&str` to `{integer}`
--> main.rs:2:19
|
2 | let total = 2 + "3";
| ^ no implementation for `{integer} + &str`
|
= help: the trait `Add<&str>` is not implemented for `{integer}`
= help: the following other types implement trait `Add<Rhs>`:
`&f32` implements `Add<f32>`
...
and 56 others
{integer} means "some integer type, not decided yet". The help: about traits will make sense in module 10 (it means: there is no definition of + for these two types). You can already use the headline and the span to fix it. This illustrates an important habit: you do not have to understand every line of a message to act on it. Start with the headline and the primary span; read notes when you need more.
Format string mismatch. println! checks its placeholders at compile time:
error: 2 positional arguments in format string, but there is 1 argument
--> main.rs:2:15
|
2 | println!("{} and {}", 1);
| ^^ ^^ -
Unbalanced brackets. A missing ) confuses the parser, and the message describes how the delimiters failed to pair up:
error: mismatched closing delimiter: `}`
--> main.rs:2:13
|
1 | fn main() {
| - closing delimiter possibly meant for this
2 | println!("hi";
| ^ unclosed delimiter
3 | }
| ^ mismatched closing delimiter
Warnings
A warning means "this compiles, but it looks wrong":
fn main() {
let unused = 10;
println!("ok");
}
warning: unused variable: `unused`
--> main.rs:2:9
|
2 | let unused = 10;
| ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
|
= note: `#[warn(unused_variables)]` on by default
warning: 1 warning emitted
The program is built and runs. The note: names the lint that produced the warning, unused_variables. Lints can be switched off for a piece of code with an attribute such as #[allow(unused_variables)], but the better response is nearly always to fix the code or, for a deliberately unused name, use the _ prefix the help suggests. In this course the checker shows you warnings but does not fail on them.
A method for fixing errors
- Fix the first error first. One mistake can cause several messages; a missing bracket can produce a cascade of nonsense further down. Fix the top one, rebuild, repeat.
- Read the headline and the primary span. Say in your own words what rule was broken.
- Read the help. It is right far more often than not. Do not apply it blindly, though; make sure you understand why it fixes the problem, because sometimes the real mistake is elsewhere (for example, the variable you meant had a different name entirely).
- Use
rustc --explainfor any code you do not recognize. - Change one thing at a time and rebuild. Rebuilding is cheap; confusion is expensive.
Common mistakes
- Skimming past the location. The line and column are exact. Go there before you think.
- Fixing the last error first. Later errors are often consequences of earlier ones.
- Treating
help:as an order. It is a suggestion from a tool that guesses your intent; it can be wrong when your intent was something else. - Ignoring warnings. A program with twenty warnings hides the one that matters.
Recap
- An error message has a severity, often a code, a headline, a location, and annotated spans.
rustc --explain EXXXXexplains any error code with examples.- Errors stop the build; warnings do not, but deserve attention.
- Fix from the top, one change at a time, reading headline, span and help in that order.
Going further: run rustc --explain E0382 now. It describes "use of a moved value", the error you will meet most in module 4. It will not fully make sense yet; come back to it then and see how much clearer it has become.