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.
The error message
Section titled “The error message”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());}Same message, different code
Section titled “Same message, different code”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 likethe 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 sayscannot satisfy `<_ as FromStr>::Err == _`, which is a scary way of saying “parse into what?”. - E0790:
let settings = Default::default();. The headline readscannot 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.
What it means
Section titled “What it means”Look at the two marks under line 2:
^^^^^^^^^^undermut scores: this is the variable whose type Rust couldn’t work out.----------underVec::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.- The help suggests
let mut scores: Vec<T>. HereTis just a placeholder. You replace it with a real type, likeVec<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.
Why Rust does this
Section titled “Why Rust does this”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.
How to fix it
Section titled “How to fix it”Fix 1: Annotate the variable
Section titled “Fix 1: Annotate the variable”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: 0Next year you'll be 43["red", "green", "blue"]NoneYou 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.
Fix 2: Use the turbofish ::<>
Section titled “Fix 2: Use the turbofish ::<>”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: 26The 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- CorroOrder 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:
NoneSettings { volume: 0, muted: false }Backup volume: 0Settings::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.
Where you’ll usually see it
Section titled “Where you’ll usually see it”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.
Related errors
Section titled “Related errors”- 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 aVec<&str>; parse each item first). - E0599: no method found, for example when you annotate
let n: u32and then call aStringmethod 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
Section titled “Quick check”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
Read more
Section titled “Read more”- E0282 — Rust error codes index: the official explanation of this error, with more examples.
- E0283 — Rust error codes index: the variant you’ll see with
collectandsum, when many types would fit. - Data Types — The Rust Book: explains annotations, including the classic
"42".parse()example. - Inference — Rust by Example: a short example of Rust inferring a
Vec’s type from later code. Iterator::collect— standard library docs: shows both the annotation style and the turbofish style.