Rust Error E0283: Type Annotations Needed for sum, collect and into
E0283 means Rust knows what your value must be able to do, but many types can do it, and Rust won’t pick one for you. You’ll usually meet it right after .sum(), .collect() or .into(). It’s one of the friendliest errors to fix: you name the type you want, and you’re done. For the other errors in this series, see Rust compiler errors.
The error message
Section titled “The error message”error[E0283]: type annotations needed --> src/main.rs:3:9 |3 | let total = prices.iter().sum(); | ^^^^^ --- type must be known at this point | = note: the type must implement `Sum<&i32>` = help: the following types implement trait `Sum<A>`: `Duration` implements `Sum<&'a Duration>` `Duration` implements `Sum` `Option<T>` implements `Sum<Option<U>>` `Result<T, E>` implements `Sum<Result<U, E>>` `Saturating<u128>` implements `Sum<&'a Saturating<u128>>` `Saturating<u128>` implements `Sum` `Saturating<u16>` implements `Sum<&'a Saturating<u16>>` `Saturating<u16>` implements `Sum` and 88 othersnote: required by a bound in `std::iter::Iterator::sum`help: consider giving `total` an explicit type |3 | let total: /* Type */ = prices.iter().sum(); | ++++++++++++This is the program that produces it:
fn main() { let prices = vec![250, 120, 99]; // prices in cents let total = prices.iter().sum(); println!("Total: {total} cents");}.collect() and .into() give the same error with a different note. For let words = text.split(' ').collect(); the note is the type must implement `FromIterator<&str>`. For let name = "Ferris".into(); it is the type must implement `From<&str>`.
What it means
Section titled “What it means”Read the message from top to bottom:
^^^^^undertotal: this is the variable whose type Rust couldn’t decide.---undersum: “type must be known at this point”.sumcan return many different types, so Rust needs the answer here.- The note:
the type must implement `Sum<&i32>`. This is the rule Rust found: “totalmust be a type that can be made by adding up&i32values”. - The help list: the types that know how to be “a sum of something”. There are many of them: every number type,
Duration, evenOptionandResult. Rust shows eight and says “and 88 others”. That’s what people mean by multiple impls satisfy this bound: more than one type has animplthat follows the rule.
Imagine asking a café for “something to drink”. That’s a real rule (it must be drinkable), but tea, juice and water all qualify. The waiter can’t bring anything until you choose. E0283 is Rust holding the tray and waiting for your answer.
Why Rust does this
Section titled “Why Rust does this”sum, collect and into are generic in their return type. The type that you ask for decides what the function does. The trait (Sum, FromIterator, From) is only the rule. So when nothing tells Rust the type, it has a rule but no single answer.
And the answer matters. Collecting the same words into a Vec<&str> keeps duplicates, but collecting them into a HashSet<&str> removes them. Summing into a u8 overflows at 255, while a u64 doesn’t. If Rust picked a type for you, a tiny edit somewhere else could quietly change how your program behaves. Rust prefers to ask once.
The way Rust works out types on its own (type inference) is explained on the E0282 page, so it isn’t repeated here. For the traits behind these methods, see Traits and Generics.
E0282, E0283, E0284: one problem, three codes
Section titled “E0282, E0283, E0284: one problem, three codes”All three say “type annotations needed”, and for you they mean the same thing: write the type. The code only tells you what Rust was doing when it got stuck:
- E0282: Rust has no clue at all about the type, or it needed the type before reaching the clue (for example, to look up a method like
.len()). - E0283 (this page): Rust has a clue in the form of a trait rule (
must implement `Sum<&i32>`), but many types follow that rule. - E0284: the rule is about a type that belongs to the unknown type, like the error type of
parse(cannot satisfy `<_ as FromStr>::Err == _`). Rust can’t check it until it knows the main type.
The code can even change while you type. let words = text.split(' ').collect(); followed by println!("{:?}", words) is E0283. Change the next line to words.len() and it becomes E0282, because now Rust needs the type to find the method. The fixes below work for all three.
How to fix it
Section titled “How to fix it”Fix 1: Annotate the variable
Section titled “Fix 1: Annotate the variable”Choose this most of the time. Add : Type after the variable name. It’s short, and it tells the next reader exactly what you’re building:
use std::collections::HashSet;
fn main() { let prices = vec![250, 120, 99]; let total: u32 = prices.iter().sum();
let tags = "rust,web,rust,cli"; let list: Vec<&str> = tags.split(',').collect(); let unique: HashSet<&str> = tags.split(',').collect();
println!("Total: {total} cents"); println!("{:?}", list); println!("Unique tags: {}", unique.len());}Output:
Total: 469 cents["rust", "web", "rust", "cli"]Unique tags: 3Same collect(), two different results: the type you write really is the instruction. You can leave the inside to Rust with _, as in let list: Vec<_> = ....
Fix 2: Use the turbofish on sum and collect
Section titled “Fix 2: Use the turbofish on sum and collect”Choose this when there’s no variable to annotate: inside a println!, in the middle of an iterator chain, or as a function argument. Put the type on the call with ::<Type> (the “turbofish”):
fn main() { let prices = vec![250, 120, 99]; println!("Total: {} cents", prices.iter().sum::<u32>());
let line = "tea,cake,soup"; let items = line.split(',').collect::<Vec<_>>(); let initials = line.split(',').map(|s| &s[..1]).collect::<String>();
println!("{:?}", items); println!("Initials: {initials}");}Output:
Total: 469 cents["tea", "cake", "soup"]Initials: tcs.product::<u64>() works the same way. Fix 1 and Fix 2 are equally correct, so pick the one that reads better.
Fix 3: For .into(), name the target type (the turbofish won’t work)
Section titled “Fix 3: For .into(), name the target type (the turbofish won’t work)”Choose this for .into() and .try_into(). It’s tempting to write "Ferris".into::<String>(), but that’s a different error, because the type parameter belongs to the Into trait, not to the method:
error[E0107]: method takes 0 generic arguments but 1 generic argument was supplied --> src/main.rs:2:25 |2 | let name = "Ferris".into::<String>(); | ^^^^ expected 0 generic argumentsInstead, annotate the variable, or call From on the target type directly, which reads nicely too:
fn main() { let name: String = "Ferris".into(); let city = String::from("Lisbon");
let small: u8 = 200; let big: u64 = small.into(); let also_big = u64::from(small);
let level: i64 = 42; let byte: u8 = level.try_into().unwrap(); let too_big = u8::try_from(300_i64);
println!("{name} lives in {city}"); println!("{big} {also_big} {byte}"); println!("300 fits in a u8? {}", too_big.is_ok());}Output:
Ferris lives in Lisbon200 200 42300 fits in a u8? falseString::from(...) and u64::from(...) say the target type right at the start of the line, so many people prefer them over .into() when there’s no other clue. See Conversions.
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 value goes somewhere with a known type anyway, like a function parameter or a struct field. Rust reads the whole function, so that later use is enough:
fn print_receipt(total: u32, items: Vec<&str>) { println!("{} items, {} cents", items.len(), total);}
fn main() { let prices = vec![250, 120, 99]; let total = prices.iter().sum(); // u32, thanks to print_receipt let items = "tea,cake,soup".split(',').collect(); // Vec<&str>, same reason print_receipt(total, items);}Output:
3 items, 469 centsNot every use counts. if prices.iter().sum() > 400 is still E0283, because > can compare many pairs of types. Passing the value to a generic parameter like impl Display doesn’t help either, since many types are Display. The use must name one exact type.
Where you’ll usually see it
Section titled “Where you’ll usually see it”.collect() straight into println!. println!("{:?}", line.split(',').collect()); gives E0283, and rustc suggests the fix itself: .collect::<Vec<_>>().
.try_into().unwrap(). let small = big.try_into().unwrap(); gives E0283 with the type must implement `TryFrom<i64>`. Fix: let small: u8 = big.try_into().unwrap(); or u8::try_from(big).unwrap().
Default::default() passed to a generic function. With fn show(value: impl Debug), the call show(Default::default()) is E0283: Rust can’t tell whose default you want. Fix: show(Settings::default()) or show(u32::default()). On its own line, let s = Default::default(); gives E0790 instead; see Default.
Your own generic function. If a type parameter only appears in the return type, like fn make<T: Default>() -> T, then let v = make(); is E0283. Fix: make::<Settings>() or let v: Settings = make();.
Related errors
Section titled “Related errors”- E0282: type annotations needed, when Rust has no clue at all about the type (or needs it for a method call).
- E0284: type annotations needed with
cannot satisfy `<_ as FromStr>::Err == _`, nearly always from.parse(). - E0790:
Default::default()(or another trait function) called without saying which type. - E0277: you named a type, but it can’t be built that way, like
let n: Vec<i32> = "1 2".split(' ').collect();. - E0107: wrong number of generic arguments, which is what
.into::<String>()gives you. - All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. What does E0283's note the type must implement Sum<&i32> tell you?
2. Which line fixes let name = "Ferris".into();?
3. Why does collecting the same words give different results into Vec<&str> and HashSet<&str>?
Score: 0 / 3
Read more
Section titled “Read more”- E0283 — Rust error codes index: the official explanation, with the classic example: should reversed
chars()become aVec<char>or aString? - Processing a Series of Items with Iterators — The Rust Book: how
sumandcollectconsume an iterator, with annotated examples. Iterator::collect— standard library docs: shows the annotation style and the turbofish style side by side.Sum— standard library docs: the trait behind.sum(), and the full list of types that implement it.Into— standard library docs: why you implementFromand call.into(), and how the target type is chosen.