Rust Error E0284: Type Annotations Needed for parse()
E0284 almost always means one thing: you called .parse() and Rust doesn’t know what to parse the text into. The note cannot satisfy `<_ as FromStr>::Err == _` looks frightening, but the fix is usually a few characters, like : u32 or ::<u32>. This page gives you the fix first, then decodes that note so it stops being scary. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0284]: type annotations needed --> src/main.rs:2:9 |2 | let age = "42".parse().unwrap(); | ^^^ ----- type must be known at this point | = note: cannot satisfy `<_ as FromStr>::Err == _`help: consider giving `age` an explicit type |2 | let age: /* Type */ = "42".parse().unwrap(); | ++++++++++++This is the program that produces it:
fn main() { let age = "42".parse().unwrap(); println!("Next year you'll be {}", age + 1);}You’ll see the same note with .expect(...) instead of .unwrap(), with match "42".parse() { ... }, and with .parse() inside a closure. When .parse() isn’t stored in a variable, rustc points at parse itself and suggests parse::</* Type */>().
What it means
Section titled “What it means”The marks are the easy part:
^^^underage: the variable whose type is unknown.-----underparse: “type must be known at this point”. This is where Rust got stuck.- The help: “consider giving
agean explicit type”. Replace/* Type */with a real type, likeu32.
Now the note, piece by piece. In compiler messages, _ means “a type I don’t know yet”:
| Piece | In plain words |
|---|---|
<_ as FromStr> |
“the type you’re parsing into (I don’t know it yet)” |
::Err |
“…and the error type that comes with it” |
== _ |
“must equal some other type I also don’t know” |
So the whole note says: “I can’t even work out what kind of error parse could return, because I don’t know what you’re parsing into.”
Think of handing a translator a letter and saying “translate this”. Into French? Japanese? Until you say, they can’t start, and they can’t even tell you what mistakes they might run into.
Why Rust does this
Section titled “Why Rust does this”Here is the signature of parse, slightly simplified:
fn parse<F: FromStr>(&self) -> Result<F, F::Err>F is the type you want back, and F::Err is its error type. Every target type brings its own: parsing an i32 can fail with a ParseIntError, an f64 with a ParseFloatError, a bool with a ParseBoolError. Rust needs F to know what parse returns at all.
The choice also changes what happens at runtime. "42" parses fine as a u8, an i32 or an f64. But "300" fails as a u8 (too big), and "4.5" fails as an i32 (not a whole number). Rust won’t guess, because a wrong guess would turn into a bug that only shows up with certain input. The Conversions lesson shows how FromStr works, and how to add it to your own types.
How Rust works out types by itself is explained on the E0282 page. Why there are three different codes (E0282, E0283, E0284) for “type annotations needed” is explained on the E0283 page. The short version: E0284 is the variant where the stuck rule is about a type that belongs to the unknown type (here F::Err). For you, the fix is the same: name the type.
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. It’s the style the Rust Book uses, and it documents what the text should contain:
fn main() { let age: u32 = "42".parse().unwrap(); let price: f64 = "4.99".parse().unwrap(); let port: u16 = "8080".parse().expect("port must be a number");
println!("Next year you'll be {}", age + 1); println!("Two coffees: {:.2}", price * 2.0); println!("Listening on {port}");}Output:
Next year you'll be 43Two coffees: 9.98Listening on 8080Pick the type that matches what the text means: u16 for a port, u8 for a percentage, f64 for a price. A too-small type turns valid input into an error, so when in doubt, choose a roomier type like i64 or u32.
Fix 2: Use the turbofish parse::<T>()
Section titled “Fix 2: Use the turbofish parse::<T>()”Choose this when there’s no variable to annotate: inside a closure, in the middle of an iterator chain, or in an expression like ... * 2:
fn main() { let line = "3 7 5"; let total = line .split(' ') .map(|s| s.parse::<u32>().unwrap()) .sum::<u32>(); let doubled = "21".parse::<i32>().unwrap() * 2;
println!("Total: {total}"); println!("Doubled: {doubled}");}Output:
Total: 15Doubled: 42Put the turbofish on parse itself. Annotating only the final result is often not enough inside a chain; see the first item under “Where you’ll usually see it” below.
Fix 3: Name the type and handle the error properly
Section titled “Fix 3: Name the type and handle the error properly”Choose this for real input (files, command-line arguments, web forms), where .unwrap() would crash your program on bad text. Name the type on parse and match on the Result. Notice how the type decides which inputs succeed:
fn main() { for input in ["42", "300", "abc"] { match input.parse::<u8>() { Ok(n) => println!("{input}: got {n}"), Err(e) => println!("{input}: {e}"), } }
let rate: Result<f64, _> = "0.25".parse(); println!("{:?}", rate);}Output:
42: got 42300: number too large to fit in target typeabc: invalid digit found in stringOk(0.25)In Result<f64, _>, you only name the value type. The _ lets Rust fill in the error type (ParseFloatError), which is exactly the part the note was asking about. More on this in Result.
Fix 4: Use the value where its type is fixed
Section titled “Fix 4: Use the value where its type is fixed”Choose this when the parsed value goes into a struct field or a function parameter with a known type. Rust reads the whole function, so that later use answers the question:
struct Config { port: u16, workers: u8,}
fn greet(age: u32) { println!("Welcome! You are {age}.");}
fn main() { let port = "8080".parse().unwrap(); // u16, from Config let workers = "4".parse().unwrap(); // u8, from Config let config = Config { port, workers }; println!("Port {} with {} workers", config.port, config.workers);
let age = "42".parse().unwrap(); // u32, from greet greet(age);}Output:
Port 8080 with 4 workersWelcome! You are 42.Not every use counts, which is why this is Fix 4. Math like age + 1 and comparisons like age >= limit still give E0284, even when limit is a u32: operators work between many pairs of types, so they don’t name one exact type. And calling a method, like age.pow(2), turns the error into E0282. When in doubt, use Fix 1.
Where you’ll usually see it
Section titled “Where you’ll usually see it”Parsing inside map, then sum(). let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); is still E0284, even though total has a type. A u32 can be the sum of u32 values or of &u32 values, so the annotation doesn’t tell Rust what each item is. Fix: s.parse::<u32>(). (Collecting into Vec<i32> instead does work, because a Vec<i32> can only hold i32.)
Collecting parse results into a Result. let nums: Result<Vec<_>, _> = text.split(' ').map(|x| x.parse()).collect(); is E0284: the _ in Vec<_> leaves the item type open. Fix: Result<Vec<i32>, _>. This handy pattern stops at the first bad item and gives you its error.
Reading user input. let guess = line.trim().parse().unwrap(); in a guessing game or a command-line tool. Fix: let guess: u32 = ..., exactly as in the Rust Book.
With ? you get a stranger error. let port = text.parse()?; doesn’t give E0284. In edition 2021, Rust falls back to the type () and reports E0277: the trait bound `(): FromStr` is not satisfied (edition 2024 says ! instead of ()). Same fix: let port: u16 = text.parse()?;.
Related errors
Section titled “Related errors”- E0282: type annotations needed, when there’s no clue at all (or the type is needed for a method call like
age.pow(2)). - E0283: type annotations needed after
.sum(),.collect()or.into(), when many types fit. It also explains why these are three different codes. - E0790:
FromStr::from_str("42")orDefault::default()called without saying which type. - E0277: what
.parse()?gives you without a type, and what you get when the type you chose can’t be parsed from text. - E0599: no method found, for example calling
.parse()on something that isn’t text, like au32variable (convert it withu8::try_from(n)instead). - All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. What does cannot satisfy <_ as FromStr>::Err == _ mean in plain words?
2. Which line fixes let port = "8080".parse().unwrap();?
3. let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); still gives E0284. Where should the type go?
Score: 0 / 3
Read more
Section titled “Read more”- E0284 — Rust error codes index: the official explanation of this error.
str::parse— standard library docs: the method’s signature, with both the annotation and turbofish styles.FromStr— standard library docs: the trait behindparse, including itsErrtype and how to implement it.- Programming a Guessing Game — The Rust Book: where
let guess: u32 = guess.trim().parse()first appears, explained step by step. - Data Types — The Rust Book: why
"42".parse()needs a type annotation, with the real compiler error.