Skip to content
Select theme

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 everyday version: a binding was declared with one type, and the value assigned to it has another.

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:

This prints:

100

If 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:

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:

This prints:

8

The pair that collides most often in real projects is String and &str. They look interchangeable and are not:

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:

This prints:

alice

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

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:

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 type
2 | 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:

This prints:

100
0
0
  • expected (), found ... — same code E0308, but when the expected type is the unit type the cause is almost always a missing semicolon or a missing return; 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.