The Error
You're compiling a Rust project and the build stops dead:
error[E0282]: type annotations needed
--> src/main.rs:4:9
|
4 | let x = [].into_iter().collect();
| ^ cannot infer type for type parameter `T`
|
= note: cannot satisfy `_: Iterator`
rustc works hard to infer types on its own. When there's nothing downstream to pin down what T should be, it stops and asks you to be explicit.
Why This Happens
Rust's type inference flows in both directions through your code. The problem hits when a generic function or trait method returns T, but nothing you do with that value narrows it down. The compiler can't guess โ it demands a concrete type.
Common triggers:
- Calling
.collect()without storing into a typed variable - Calling
.parse()on a string with no target type - Using
Default::default()orFromStr::from_str()with no type hint - Empty literals like
vec![]or[]โ no elements means no inferred type - Generic functions where the return type depends purely on a type parameter
Step-by-Step Fix
1. Annotate the variable binding
Add the type directly to the let binding. Nine times out of ten, this is all you need.
// Before โ compiler can't infer the collected type
let numbers = "1 2 3".split_whitespace().map(|s| s.parse().unwrap()).collect();
// After โ specify the collection type
let numbers: Vec<i32> = "1 2 3".split_whitespace().map(|s| s.parse().unwrap()).collect();
2. Use turbofish syntax
Skip the binding annotation entirely. Attach the type at the call site with turbofish (::<T>) instead. This is handy when you pass the result directly into another function without storing it first.
// parse() needs to know what to parse into
let n = "42".parse::<i32>().unwrap();
// collect() needs to know the target collection
let v = (0..5).map(|x| x * 2).collect::<Vec<_>>();
// Parsing an IP address
let addr = "127.0.0.1".parse::<std::net::IpAddr>().unwrap();
3. Fix empty collection literals
An empty Vec::new() gives the compiler zero clues. Pick one of these three approaches:
// Error: no element type to infer from
let mut items = Vec::new();
// Option 1: annotate the binding
let mut items: Vec<String> = Vec::new();
// Option 2: turbofish on the constructor
let mut items = Vec::<String>::new();
// Option 3: push an element first (compiler infers T = String)
let mut items = Vec::new();
items.push(String::from("hello"));
4. Fix with Default::default()
// Error โ what type should this be?
let config = Default::default();
// Fix: annotate the binding
let config: MyConfig = Default::default();
// Works for standard types too
let counts: HashMap<String, i32> = Default::default();
5. Fix in generic function calls
When a generic function's return type can't be inferred from how you use the result, make it explicit at the call site:
fn make_pair<A, B>(a: A, b: B) -> (A, B) {
(a, b)
}
// Suffix literals with their type
let pair = make_pair(1i32, 2i32);
// Or turbofish the whole call
let pair = make_pair::<i32, f64>(1, 2.0);
6. Fix collect() chains
collect() is the number-one trigger for E0282. It can produce dozens of collection types โ the compiler won't pick for you. Always anchor the output type somewhere in the chain:
use std::collections::{HashMap, HashSet};
// Collect into a HashSet
let unique: HashSet<i32> = vec![1, 2, 2, 3].into_iter().collect();
// Collect into a HashMap from key-value pairs
let map: HashMap<_, _> = vec![("a", 1), ("b", 2)].into_iter().collect();
// Or turbofish directly on collect
let unique = vec![1, 2, 2, 3].into_iter().collect::<HashSet<_>>();
The _ placeholder is your friend โ specify the outer type and let the compiler fill in the rest.
Verify the Fix
Run cargo check rather than a full build โ no binary is produced, so it's faster:
cargo check
Clean output looks like this:
Checking myproject v0.1.0 (/path/to/myproject)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s
Still seeing E0282? Pipe through grep to track down every remaining occurrence:
cargo build 2>&1 | grep E0282
Quick Reference
- Variable annotation:
let x: Vec<i32> = ... - Turbofish:
method::<Type>()orType::<Param>::method() - Partial inference: use
_for parts the compiler can still figure out โVec<_>,HashMap<String, _> - Error ID: always
error[E0282]โ runrustc --explain E0282for the full compiler breakdown
When Annotations Get Verbose
Long type annotations scattered everywhere get ugly fast. A type alias cleans things up:
type WordCount = HashMap<String, usize>;
let counts: WordCount = text.split_whitespace()
.map(|w| (w.to_string(), 1))
.collect();
One alias, one annotation on the binding โ call sites stay clean and the compiler gets the hint it needs.

