Getting Started: Programs and the Compiler · about 30 min

Reading Compiler Errors

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

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

  1. 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.
  2. Read the headline and the primary span. Say in your own words what rule was broken.
  3. 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).
  4. Use rustc --explain for any code you do not recognize.
  5. Change one thing at a time and rebuild. Rebuilding is cheap; confusion is expensive.

Common mistakes

Recap

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.

Exercises

Exercise 1 · quiz

Where is the problem?

rustc prints:

error[E0425]: cannot find value `totl` in this scope
 --> main.rs:7:20
  |
7 |     println!("{}", totl);
  |                    ^^^^ help: a local variable with a similar name exists: `total`

Which statements are correct? Select all that apply.

Answer
  • The problem is on line 7, starting at column 20

  • E0425 is an error code you can look up with rustc --explain E0425

  • The compiler suggests that total may be the name that was meant

main.rs:7:20 is file, line, column. The carets underline the exact span the message is about, and the help: line is a concrete suggestion. Nothing was compiled: an error means no executable was produced at all. Only warnings let the build finish.

Exercise 2 · write code

Fix a misspelled name

This program does not compile. Read the error and fix it so the program prints:

lines: 42

File: main.rs

Starting point

fn main() {
    let line_count = 42;
    println!("lines: {}", line_cuont);
}

Example

expected output

lines: 42
Hint 1

The error is E0425, cannot find value. Compare the spelling with the let line.

Reference solution
fn main() {
    let line_count = 42;
    println!("lines: {}", line_count);
}

Exercise 3 · write code

Fix a type error

This program tries to add a number and a piece of text, which Rust does not allow. Fix it so that it compiles and prints:

5

Change as little as possible: the fix is in the expression on the let line.

File: main.rs

Starting point

fn main() {
    let total = 2 + "3";
    println!("{}", total);
}

Example

expected output

5
Hint 1

The error E0277 says there is no implementation for {integer} + &str.

Hint 2

"3" is text (a string). 3 without quotes is a number.

Reference solution
fn main() {
    let total = 2 + 3;
    println!("{}", total);
}

Exercise 4 · write code

Fix a format string

The format string here has more {} placeholders than values, so the macro refuses to compile. Fix the program so it prints exactly:

3 apples and 4 pears

File: main.rs

Starting point

fn main() {
    let apples = 3;
    let pears = 4;
    println!("{} apples and {} pears", apples);
}

Example

expected output

3 apples and 4 pears
Hint 1

Each {} needs one value after the format string, separated by commas, in order.

Reference solution
fn main() {
    let apples = 3;
    let pears = 4;
    println!("{} apples and {} pears", apples, pears);
}

Exercise 5 · quiz

Warning or error?

You build a program and rustc prints one warning: unused variable: x and nothing else. What happened?

Answer
  • The build succeeded and produced an executable; the warning points at code that is probably a mistake

Warnings never stop the build. They flag code that is legal but suspicious: a variable you created and never used often means you used the wrong variable somewhere else. Read warnings, and keep your programs warning-free so new ones stand out. To say "unused on purpose", start the name with an underscore: _x.