Skip to content

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.

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 */>().

The marks are the easy part:

  1. ^^^ under age: the variable whose type is unknown.
  2. ----- under parse: “type must be known at this point”. This is where Rust got stuck.
  3. The help: “consider giving age an explicit type”. Replace /* Type */ with a real type, like u32.

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.

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.

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 43
Two coffees: 9.98
Listening on 8080
▶ Try it Yourself

Pick 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.

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: 15
Doubled: 42
▶ Try it Yourself

Put 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 42
300: number too large to fit in target type
abc: invalid digit found in string
Ok(0.25)
▶ Try it Yourself

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 workers
Welcome! You are 42.
▶ Try it Yourself

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.

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()?;.

  • 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") or Default::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 a u32 variable (convert it with u8::try_from(n) instead).
  • All compiler errors: the full list of error pages.

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