Skip to content

← Orientation and the Gate step 2 of 6

Easy Primitives

How to read a rustc error message

Sum a slice of integers.

pub fn tally(items: &[i32]) -> i32

[1, 2, 3] is 6, [] is 0, [-5, 5] is 0.

The starter does not compile. It contains four independent mistakes, deliberately planted, and your job is to read your way out. This skill gates every other item in the course: a learner who reads the whole message stops needing hints.

The compiler will not show you everything at once

rustc runs in passes, and a pass only runs if the previous one succeeded. So you will fix errors in rounds. Concretely, for this starter:

  • Round 1 reports E0277 and E0308. Those are type errors.
  • Fix them, compile again, and round 2 reports E0282 — type inference had no chance to run until the type errors were gone.
  • Fix that, and round 3 finally reports E0384, which comes from the borrow checker, the last pass of all.

This is not the compiler being coy. It is a real ordering: you cannot check who owns what until you know what everything is. Expect it, and stop reading “1 error” as “1 mistake”.

Anatomy of a message

A rustc diagnostic has five parts, and beginners typically read one of them.

error[E0308]: mismatched types
  --> unit.rs:11:16
   |
11 |         return "nothing to tally";
   |                ^^^^^^^^^^^^^^^^^^ expected `i32`, found `&str`
   |
help: try removing the string literal and returning a number
  1. The codeE0308. Stable, searchable, and explainable: rustc --explain E0308 prints a page about it.
  2. The headline — “mismatched types”. Usually too short to act on alone.
  3. The location — file, line, column.
  4. The underline and its label — this is the part that actually tells you what happened. expected i32, found &str names both sides. Almost every beginner question is answered by this line.
  5. The help: / note: footer — frequently a literal fix.

The four codes you will meet here

E0308 — mismatched types. The most common error in Rust by a wide margin, and it has several distinct faces. Here it is a &str returned from a function declared -> i32. Elsewhere it is “if and else have incompatible types”, or the notorious semicolon swallows the return value: a function body ending in x; evaluates to (), not to x, and () is not i32. If you learn to recognise only one code, learn this one.

E0277 — the trait bound is not satisfied. In this starter it is the type `[i32]` cannot be indexed by `i32`. Slices are indexed by usize, the pointer-sized unsigned integer, and by nothing else. Item 1.3 explains why. E0277 in general means “you asked for an operation this type does not implement” — indexing, adding, printing, comparing.

E0282 — type annotations needed. Vec::new() builds a Vec<_> and Rust infers the _ from how you use it. If you never push anything in, there is nothing to infer from, and the compiler refuses to guess. The fix is one annotation: let scratch: Vec<i32> = Vec::new();.

E0384 — cannot assign twice to immutable variable. let bindings are immutable by default in Rust — the opposite default from almost every other language you have used. let mut opts in. Item 1.1 is about exactly this.

Then the gate

Once it compiles, clippy -D warnings still has to pass, and the accumulated scaffolding in the starter will not survive it. The shortest correct answer to this problem is one line long, and the standard library already contains the loop you were about to write.

Predict the error code before you compile. Then compile. You will be right more often than you expect, and being right is what makes the compiler start feeling like a colleague instead of a gatekeeper.