home ~ projects ~ socials

Rust's Error Messages are Great

Something Broke

Rust's error messages are one of my favorite features of the language. That's no accident. The compiler folks spend a ton of effort on them. Going so far as saying that any error that doesn't give you enough information to fix it is considered a bug1.

It's an amazing philosophy.

I collected a few examples below. I'm not going to do a full write up explaining the ins and outs of the rust code behind them. That's not necessary to appreciate them. You can see some of the best parts without understanding the language at all.

Line Numbers

Showing line numbers in error messages is table stakes. Different language have their own styles. Rust's approach of showing code lines with the line numbers on the left side (similar to your editor) works great.

A screen grab of an error message. The text in the image can be expanded below
Expand to view original image and error text
A screen grab of an error message. The text in the image is below
Compiling neopolitan v0.2.0 (/Users/alan/workshop/neopolitan)
error[E0425]: cannot find function `span_attr_key_token` in this scope
   --> src/span_attr/mod.rs:102:22
    |
70  | / pub fn solo_span_attr_key_token<'a>(
71  | |     source: &'a str,
72  | |     character: &'a str,
73  | | ) -> IResult<&'a str, String> {
...   |
84  | |     Ok((source, key_snippets.join("").to_string()))
85  | | }
    | |_- similarly named function `solo_span_attr_key_token` defined here
...
102 |           let result = span_attr_key_token(source, character).unwrap();
    |                        ^^^^^^^^^^^^^^^^^^^ help: a function with a similar name exists: `solo_span_attr_key_token`

For more information about this error, try `rustc --explain E0425`.
error: could not compile `neopolitan` (lib test) due to 1 previous error
[Command exited with 101]

Pointers

Rust uses the ^ character to point to the specific location on a line that has the problem. Sometimes, it's a full function name. Sometimes, it's a single character:

A screen grab of an error message. The text in the image can be expanded below
Expand to view original image and error text
A screen grab of an error message. The text in the image is below
Compiling mjoin v0.1.0 (/Users/alan/workshop/mjoin)
error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
  --> src/main.rs:37:47
   |
32 | pub fn get_output_list(root_dir: &PathBuf) -> Result<BTreeMap<PathBuf, Vec<PathBuf>>> {
   | ------------------------------------------------------------------------------------- this function returns a `Result`
...
37 |         let file_name = e.file_name().to_str()?;
   |                                               ^ use `.ok_or(...)?` to provide an error compatible with `Result<BTreeMap<PathBuf, Vec<...>>, ...>`
   |
   = note: the full name for the type has been written to '/Users/alan/workshop/mjoin/target/debug/deps/mjoin-89e7bbeab42b3660.long-type-5502576226326651282.txt'
   = note: consider using `--verbose` to print the full type name to the console

For more information about this error, try `rustc --explain E0277`.
error: could not compile `mjoin` (bin "mjoin") due to 1 previous error

Extra Details

Error messages tend to show more than just the error itself. Take this, for example:

A screen grab of an error message. The text in the image can be expanded below

The error is:

expected `(&str, String)`, found `(String, &str)`

which happens on line 99.

The extra piece of info is why it's a problem. Specifically, the solo_span_attr_key_token_valid_tests function on line 96 where:

arguments to this function are incorrect
Expand to view original image and error text
A screen grab of an error message. The text in the image is below
Compiling neopolitan v0.2.0 (/Users/alan/workshop/neopolitan)
error[E0308]: mismatched types
  --> src/span_attr/mod.rs:99:17
   |
96 |     fn solo_span_attr_key_token_valid_tests(
   |        ------------------------------------ arguments to this function are incorrect
...
99 |         #[case] left: (&str, String),
   |                 ^^^^ expected `(&str, String)`, found `(String, &str)`
   |
   = note: expected tuple `(&str, std::string::String)`
              found tuple `(std::string::String, &str)`
note: function defined here
  --> src/span_attr/mod.rs:96:8
   |
96 |     fn solo_span_attr_key_token_valid_tests(
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
99 |         #[case] left: (&str, String),
   |                 --------------------

For more information about this error, try `rustc --explain E0308`.
error: could not compile `neopolitan` (lib test) due to 1 previous error
[Command exited with 101]

Helpful Suggestions

Error messages tend to have explicit recommendations for possible fixes. For example, here's an error that occurs on line 107:

A screen grab of an error message. The text in the image can be expanded below

The text of the error defines the problem:

cannot infer type of the type parameter `T` declared on the function `channel`

The next thing in the message is a help line with:

help: consider specifying the generic argument

Even better, the message shows you an updated version of line 107 with the recommendation in place and underlined with + characters to point out the change.

Expand to view original image and error text
A screen grab of an error message. The text in the image is below
Compiling mjoin v0.1.0 (/Users/alan/workshop/mjoin)
error[E0282]: type annotations needed
   --> src/main.rs:107:20
    |
107 |     let (tx, rx) = std::sync::mpsc::channel();
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `channel`
    |
help: consider specifying the generic argument
    |
107 |     let (tx, rx) = std::sync::mpsc::channel::<T>();
    |                                            +++++

For more information about this error, try `rustc --explain E0282`.
error: could not compile `mjoin` (bin "mjoin") due to 1 previous error

Error Number Details

Each error starts with a line like:

error[E0308]: mismatched types

And has a corresponding line like this at the end:

For more information about this error, try `rustc --explain E0308`.

For example:

A screen grab of an error message. The text in the image can be expanded below

The error code has more details about why the error might have happened and possible corrective measures. As it says, you can see these details by running the specific command like:

rustc --explain E0308

Which, in the case of error E0308, outputs this:

[NOTE: Don't worry about trying to understand the output. This is about showing the amount of information Rust gives you to find a solution for an error more than anything else.]

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`

if "Not a bool" {
// ^^^^^^^^^^^^ expected `bool`, found `&str`
}

let x: f32 = "Not a float";
//     ---   ^^^^^^^^^^^^^ expected `f32`, found `&str`
//     |
//     expected due to this
```

This error occurs when an expression was used in a place where the compiler
expected an expression of a different type. It can occur in several cases, the
most common being when calling a function and passing an argument which has a
different type than the matching type in the function declaration.
Expand to view original image and error text
A screen grab of an error message. The text in the image is below
NOTE: I goofed copying the text for this image. 
It's like the others on this page. Just a 
different error.

Use Sharp Tools

Rust's initial learning curve took significant effort to get over. A big part was learning how to read the error messages. They were overwhelming at first. After putting in some reps, they're amazing. They help make the language a pleasure to use.

-a

-- end of line --

Footnotes

I got a response to a post on Mastodon from one of the compiler folks asking for bug reports on errors I didn't understand. This was some time last year. But, fucked if I can find it because Mastodon doesn't have search.