How to fix error E0308: mismatched types
Short answer: a value of one type ended up where the code expected another. Every error page in
this chapter so far has been about names — undeclared crates and
modules, unresolved
imports, values the scope never
heard of. E0308 is the other failure mode:
the name resolved fine, the compiler knows exactly what type each side has, and the two types do
not match. The diagnostic prints both — expected X, found Y — and points at the file, the line,
and the column where they disagree.
The error
Section titled “The error”The everyday version: a binding was declared with one type, and the value assigned to it has another.
fn main() { let count: u32 = 50; let total: i32 = count * 2; println!("{total}");}Compiling that with cargo build prints:
error[E0308]: mismatched types --> src/main.rs:3:22 |3 | let total: i32 = count * 2; | --- ^^^^^^^^^ expected `i32`, found `u32` | | | expected due to this |help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit |3 | let total: i32 = (count * 2).try_into().unwrap(); | + +++++++++++++++++++++
For more information about this error, try `rustc --explain E0308`.The headline states both types, expected first. The --> line names the file, the line, and the
column — the column counts characters from the start of the line and points at the first character
of the expression that has the wrong type. The snippet carries two underlines: the one under the
expression carries the expected/found label, and the one under the i32 annotation says
expected due to this — where the expectation came from.
The help: block offers a real conversion: (count * 2).try_into().unwrap(), which compiles and
panics at run time the moment the value does not fit an i32. It is the wrong medicine here,
because the u32 was the considered choice — counts are never negative — and only the program’s
intent can say so. When the annotation is the considered half, it gives way instead:
fn main() { let count: u32 = 50; let total: u32 = count * 2; println!("{total}");}This prints:
100If the value genuinely has to change type, that is a conversion — as, From, try_into — not a
looser annotation. Converting “to make the error go away” hides the mismatch; converting because
the program means it fixes it.
When the expected type comes from a signature
Section titled “When the expected type comes from a signature”No annotation has to be anywhere in sight. The expected type can be pinned down anywhere the value’s type is fixed, and the most common place is a function signature written far from the call:
fn area(width: f64, height: f64) -> f64 { width * height}
fn main() { println!("{}", area(4, 2));}Compiling that with cargo build prints:
error[E0308]: arguments to this function are incorrect --> src/main.rs:6:20 |6 | println!("{}", area(4, 2)); | ^^^^ - - expected `f64`, found integer | | | expected `f64`, found integer |note: function defined here --> src/main.rs:1:4 |1 | fn area(width: f64, height: f64) -> f64 { | ^^^^ ---------- -----------help: use a float literal |6 | println!("{}", area(4.0, 2)); | ++help: use a float literal |6 | println!("{}", area(4, 2.0)); | ++
For more information about this error, try `rustc --explain E0308`.The headline names the symptom — arguments to this function are incorrect — while the code is
still E0308. Each literal gets its own expected \f64`, found integerunderline, and thenote:points back to the signature that demandedf64. Notice the wording found integer— notfound i32: an unsuffixed numeric literal has no type of its own until one is forced on it, so the compiler names the kind, not a concrete type. The two help:blocks each add.0` to one
argument — the fix the caller always meant:
fn area(width: f64, height: f64) -> f64 { width * height}
fn main() { println!("{}", area(4.0, 2.0));}This prints:
8String and &str
Section titled “String and &str”The pair that collides most often in real projects is String and &str. They look
interchangeable and are not:
fn main() { let user = "alice"; let name: String = user; println!("{name}");}Compiling that with cargo build prints:
error[E0308]: mismatched types --> src/main.rs:3:24 |3 | let name: String = user; | ------ ^^^^ expected `String`, found `&str` | | | expected due to this |help: try using a conversion method |3 | let name: String = user.to_string(); | ++++++++++++
For more information about this error, try `rustc --explain E0308`.This time the help: block has a concrete suggestion — try using a conversion method — and it
is the right one: a &str becomes an owned String with .to_string(). Accept it:
fn main() { let user = "alice"; let name: String = user.to_string(); println!("{name}");}This prints:
aliceThe reverse fix is just as legitimate: if nothing needs to own the text, change the annotation to
&str and skip the allocation. What E0308 will not do is decide ownership for you — that is what
the annotation was asking for in the first place.
The return type pins the tail expression
Section titled “The return type pins the tail expression”A function’s tail expression must have the type the -> declares, and the expectation again
travels from a line the reader is not looking at:
fn score(raw: &str) -> u32 { let n: i32 = raw.trim().parse().unwrap_or(0); n}
fn main() { println!("{}", score("100"));}Compiling that with cargo build prints:
error[E0308]: mismatched types --> src/main.rs:3:5 |1 | fn score(raw: &str) -> u32 { | --- expected `u32` because of return type2 | let n: i32 = raw.trim().parse().unwrap_or(0);3 | n | ^ expected `u32`, found `i32` |help: you can convert an `i32` to a `u32` and panic if the converted value doesn't fit |3 | n.try_into().unwrap() | ++++++++++++++++++++
For more information about this error, try `rustc --explain E0308`.The pointer is on the tail expression, and the note pins the expectation to its source:
expected \u32` because of return type, under the -> u32in the signature. Thehelp:block once again offers a panickingtry_into().unwrap(). The fix that changes less is to parse straight into the return type — which also changes the behavior for the better, because a score of -7ornopebecomes0` instead of a crash:
fn score(raw: &str) -> u32 { raw.trim().parse::<u32>().unwrap_or(0)}
fn main() { println!("{}", score("100")); println!("{}", score("-7")); println!("{}", score("nope"));}This prints:
10000Not this error
Section titled “Not this error”expected (), found ...— same code E0308, but when the expected type is the unit type the cause is almost always a missing semicolon or a missingreturn; that failure gets its own treatment in this chapter.- E0277 — when the type is fine but it does not implement a trait the code requires, the code
is different (
the trait bound ... is not satisfied) and so is the fix.