Skip to content

Rust Error E0282: Type Annotations Needed

E0282 means Rust tried to figure out a type by itself and didn’t find enough clues. Nothing is wrong with your logic. The compiler just needs you to name one type, usually with a few extra characters. This page shows the fix first, then explains how Rust’s type guessing (called type inference) works, so you’ll know when to help it. For the other errors in this series, see Rust compiler errors.

error[E0282]: type annotations needed for `Vec<_>`
--> src/main.rs:2:9
|
2 | let mut scores = Vec::new(); // a Vec of... what?
| ^^^^^^^^^^ ---------- type must be known at this point
|
help: consider giving `scores` an explicit type, where the type for type parameter `T` is specified
|
2 | let mut scores: Vec<T> = Vec::new(); // a Vec of... what?
| ++++++++

This is the program that produces it:

fn main() {
let mut scores = Vec::new(); // a Vec of... what?
println!("Scores recorded: {}", scores.len());
}

The words “type annotations needed” also appear with three other codes. They mean the same thing, and the fixes on this page work for all of them:

  • E0283: .collect() or .sum() without a target type. rustc adds a note like the type must implement `FromIterator<&str>` or `Sum<&i32>`, because many types could be the result.
  • E0284: "42".parse().unwrap() without a target type. The note says cannot satisfy `<_ as FromStr>::Err == _`, which is a scary way of saying “parse into what?”.
  • E0790: let settings = Default::default();. The headline reads cannot call associated function on trait without specifying the corresponding `impl` type: Rust doesn’t know whose default you want.

Which code you get depends on the exact line, and it can change if you add more code. For example, let age = "42".parse().unwrap(); gives E0284, but if the next line calls age.pow(2), it becomes E0282. Don’t worry about the number; look for the variable rustc points at.

Look at the two marks under line 2:

  1. ^^^^^^^^^^ under mut scores: this is the variable whose type Rust couldn’t work out.
  2. ---------- under Vec::new(): “type must be known at this point”. Vec::new() makes an empty list, but an empty list of what? Numbers? Names? Rust needs to know to reserve the right kind of storage.
  3. The help suggests let mut scores: Vec<T>. Here T is just a placeholder. You replace it with a real type, like Vec<u32>.

Think of a box at the post office with the “contents” field left blank. The clerk can’t send it until somebody writes something there. You can write it yourself, or put something in the box so the clerk can see.

Every value in Rust has one exact type, decided at compile time. Most of the time you don’t write it, because Rust reads your code and works it out. let count = 5; is an i32. let name = String::from("Ferris"); is a String. That’s type inference, and the Variables lesson shows more of it.

Inference uses clues from the whole function, not just one line. If you call scores.push(90) later, that’s a clue: the Vec holds numbers. If you pass scores to a function that takes &[u32], that’s an even better clue. E0282 appears when there are no clues at all, or when Rust needs the type before it reaches the clue (for example, to look up a method).

Some functions are generic in their return type: parse, collect, sum, Default::default, Vec::new, None. They can produce many different types, so Rust can’t pick one for you. It won’t guess, because a wrong guess would silently change how your program behaves. See Generics for how one function can return many types.

Choose this in most cases. Add : Type after the variable name. It’s short, and it also documents your code for the next reader:

fn main() {
let scores: Vec<u32> = Vec::new();
let age: u32 = "42".parse().unwrap();
let words: Vec<&str> = "red green blue".split(' ').collect();
let winner: Option<String> = None;
println!("Scores recorded: {}", scores.len());
println!("Next year you'll be {}", age + 1);
println!("{:?}", words);
println!("{:?}", winner);
}

Output:

Scores recorded: 0
Next year you'll be 43
["red", "green", "blue"]
None
▶ Try it Yourself

You don’t always have to write the full type. let words: Vec<_> = ...collect(); means “a Vec, and you figure out what goes inside”. The _ asks Rust to fill in that part. More on writing types in Data types.

Choose this when there’s no variable to annotate, for example in the middle of a chain of iterator calls. You put the type right on the function call with ::<Type>. People call ::<> the “turbofish” because ::<> looks a bit like a fish:

fn main() {
let input = "4 15 7";
let numbers = input
.split(' ')
.map(|s| s.parse::<u32>().unwrap())
.collect::<Vec<_>>();
let total = numbers.iter().sum::<u32>();
println!("{:?}", numbers);
println!("Total: {total}");
}

Output:

[4, 15, 7]
Total: 26
▶ Try it Yourself

The three most common turbofish calls are parse::<i32>(), collect::<Vec<_>>() and sum::<i32>(). Fix 1 and Fix 2 are equally correct; let total: u32 = numbers.iter().sum(); does the same job. Pick whichever reads better.

Fix 3: Use the value, so Rust can infer the type

Section titled “Fix 3: Use the value, so Rust can infer the type”

Choose this when the error appears because your code isn’t finished yet. Often you create an empty Vec, and the code that fills it comes later. Once you use the value, Rust has its clue and the error goes away, with no annotation needed:

fn print_team(names: &[String]) {
for name in names {
println!("- {name}");
}
}
fn main() {
let mut team = Vec::new(); // type unknown... for now
team.push(String::from("Ferris")); // now Rust knows: Vec<String>
team.push(String::from("Corro"));
print_team(&team);
}

Output:

- Ferris
- Corro
▶ Try it Yourself

Order matters a little. The clue must come before Rust needs the type to call a method. A closure like let shout = |s| s.to_uppercase(); still fails even if you call shout("hi") on the next line, because Rust must know what s is to find .to_uppercase(). There, write the type: |s: &str| s.to_uppercase(). See Closures.

Fix 4: Name the type for None and Default::default()

Section titled “Fix 4: Name the type for None and Default::default()”

Choose this for values that carry no clue at all. None doesn’t say what it would hold, and Default::default() doesn’t say whose default you want. Write the type into the expression:

#[derive(Debug, Default)]
struct Settings {
volume: u8,
muted: bool,
}
fn main() {
let fastest_lap = None::<f64>; // instead of: None
let settings = Settings::default(); // instead of: Default::default()
let backup: Settings = Default::default(); // an annotation works too
println!("{:?}", fastest_lap);
println!("{:?}", settings);
println!("Backup volume: {}", backup.volume);
}

Output:

None
Settings { volume: 0, muted: false }
Backup volume: 0
▶ Try it Yourself

Settings::default() is the clearest choice: it reads as “the default Settings”. None::<f64> is handy, but let fastest_lap: Option<f64> = None; (Fix 1) is more common. See Option.

Parsing text into a number. let guess = line.trim().parse().unwrap(); gives E0284 (or E0282 if you call a method on guess next). parse can produce an i32, a u8, an f64 and many more. Fix: let guess: i32 = ... or .parse::<i32>(). Comparing guess with an i32 variable later is not enough here (passing it to a function that takes an i32 would be), so the simplest fix is to name the type right at the call. E0284 explains this case in detail.

.collect(). It can build a Vec, a String, a HashSet, a HashMap… so it always needs to know the target. Fix: let words: Vec<_> = ...collect(); or .collect::<Vec<_>>(). See Iterators.

.sum() and .product(). nums.iter().sum() gives E0283 with the type must implement `Sum<&i32>`. Adding up i32s could give an i32, but other number types can be sums too. Fix: let total: i32 = nums.iter().sum(); or .sum::<i32>().

An empty collection in a struct constructor. let cache = HashMap::new(); in a new() function that you haven’t finished writing. Fix: write the full type once (HashMap<String, u32>), or return it in a struct whose field type is already declared, which is a clue Rust can use.

  • E0308: mismatched types. This happens when your annotation and the value disagree, like let age: u32 = "42";.
  • E0277: trait bound not satisfied. You gave a type, but it can’t be built that way, like let n: Vec<i32> = "1 2".split(' ').collect(); (that’s a Vec<&str>; parse each item first).
  • E0599: no method found, for example when you annotate let n: u32 and then call a String method like .trim() on it.
  • E0004: non-exhaustive patterns, another error where rustc writes the fix for you.
  • All compiler errors: the full list of error pages.

Quick check

1. Which line fixes E0282 on let ids = Vec::new(); when the Vec is meant to hold u64 values?

2. Why does let n = "7".parse().unwrap(); need a type, even though the text is clearly a number?

3. What does the _ mean in .collect::<Vec<_>>()?

Score: 0 / 3