Rust Error E0308: Mismatched Types
Error E0308 means Rust expected a value of one type, but you gave it a value of a different type. It’s probably the error you’ll see most often in Rust, and the message almost always tells you both types, so it’s usually quick to fix. (Looking for a different error? See the compiler errors overview.)
The error message
Section titled “The error message”error[E0308]: mismatched types --> src/main.rs:6:29 |6 | let user = User { name: "Ferris" }; | ^^^^^^^^ expected `String`, found `&str` |help: try using a conversion method |6 | let user = User { name: "Ferris".to_string() }; | ++++++++++++Here is the program that produces it:
struct User { name: String,}
fn main() { let user = User { name: "Ferris" }; println!("{}", user.name);}What it means
Section titled “What it means”Every E0308 message has the same shape: expected A, found B.
- Expected is the type Rust needs at that spot. It comes from somewhere else: a type annotation, a function’s parameter or return type, a struct field, or another branch of an
if/match. - Found is the type of the value you actually wrote there.
In the example, the name field was declared as String, so that’s what Rust expects. But "Ferris" is a string literal, which has the type &str. They are related, but they are not the same type.
Look for the help: lines too. For E0308 the compiler often suggests the exact fix, like .to_string() above.
Why Rust does this
Section titled “Why Rust does this”Rust never converts between types silently. In some languages a number quietly becomes a string, or a big number is quietly cut down to fit a smaller one. Those hidden conversions cause real bugs: lost data, wrong results, crashes far away from the actual mistake.
So Rust makes every conversion visible in the code. It’s a bit more typing, but when you read the code later you can see exactly where a value changes type and whether that step can fail. See Data Types for the types themselves.
How to fix it
Section titled “How to fix it”Each fix below starts with the shape of the error, so you can jump to the one that matches yours.
Fix 1: Remove the trailing semicolon
Section titled “Fix 1: Remove the trailing semicolon”error[E0308]: mismatched types --> src/main.rs:1:46 |1 | fn total_price(price: u32, quantity: u32) -> u32 { | ----------- ^^^ expected `u32`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression2 | price * quantity; | - help: remove this semicolon to return this valueIf you see found (), check the last line of the function. In Rust, the last expression of a block without a semicolon is its return value. Adding ; turns it into a statement, so the function returns () (the “nothing” type) instead. Delete the semicolon:
fn total_price(price: u32, quantity: u32) -> u32 { price * quantity // no semicolon: this value is returned}
fn main() { println!("Total: {}", total_price(250, 3));}Output:
Total: 750The same thing happens inside if blocks and match arms. More in Functions.
Fix 2: Convert between String and &str
Section titled “Fix 2: Convert between String and &str”String is an owned, growable string. &str is a borrowed view of some text. See Strings for the full story. Converting is short in both directions:
&strtoString:.to_string()orString::from(...).Stringto&str: borrow it with&(or call.as_str()).
The second case shows up as expected &str, found String, usually when you pass a String to a function that takes &str.
struct User { name: String,}
fn greet(name: &str) { println!("Hello, {name}!");}
fn main() { // &str -> String: .to_string() or String::from() let user = User { name: "Ferris".to_string() };
// String -> &str: borrow it with & (or call .as_str()) greet(&user.name); greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new(); tags.push(String::from("rust")); println!("{tags:?}");}Output:
Hello, Ferris!Hello, Ferris!["rust"]Fix 3: Convert between number types
Section titled “Fix 3: Convert between number types”error[E0308]: mismatched types --> src/main.rs:3:22 |3 | let count: i32 = names.len(); | --- ^^^^^^^^^^^ expected `i32`, found `usize` | | | expected due to thisi32, i64, u32 and usize are all different types, and Rust won’t mix them. Lengths and indexes are always usize. Pick a conversion:
.into()(ori64::from(x)) when the conversion can never lose data, likei32toi64.i64::try_from(x)(or.try_into()) when it might not fit. It returns aResult, so you decide what happens on failure.asis the quick cast. It never fails, but it silently cuts off values that don’t fit, so use it only when you know the value is small.
fn average(total: i64, count: i64) -> i64 { total / count}
fn main() { let scores: Vec<i32> = vec![80, 95, 70]; let total: i32 = scores.iter().sum();
// i32 -> i64 always fits, so .into() is enough let total: i64 = total.into();
// usize -> i64 might not fit, so try_from asks first let count = i64::try_from(scores.len()).unwrap();
println!("Average: {}", average(total, count));
// `as` also works, but it silently cuts off values that don't fit let players = scores.len() as i32; println!("{players} players");}Output:
Average: 813 playersOften the simplest fix is to change the annotation instead: let count: usize = names.len();. The Type Casting and Conversions pages go deeper.
Fix 4: Wrap in Some, or unwrap the Option
Section titled “Fix 4: Wrap in Some, or unwrap the Option”error[E0308]: mismatched types --> src/main.rs:3:27 |3 | let position: usize = names.iter().position(|n| *n == "Ben"); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>` | | | expected due to thisOption<usize> is not a usize. It’s “maybe a usize, maybe nothing”. This mismatch goes both ways:
- Expected
T, foundOption<T>: you have to deal with the “nothing” case, usingmatch,if let, orunwrap_or(default). - Expected
Option<T>, foundT: your function returnsOption, so wrap the value:Some(10)instead of10.
fn find_discount(code: &str) -> Option<u32> { if code == "SAVE10" { return Some(10); // T -> Option<T>: wrap it in Some } None}
fn main() { let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T: handle the "nothing found" case... match names.iter().position(|n| *n == "Ben") { Some(i) => println!("Ben is at {i}"), None => println!("Ben isn't here"), }
// ...or choose a default value with unwrap_or let discount: u32 = find_discount("HELLO").unwrap_or(0); println!("Discount: {discount}%");}Output:
Ben is at 1Discount: 0%The compiler may suggest .expect("REASON"). That works too, but it crashes the program when the value is None, so only use it when None really can’t happen. See Option.
Fix 5: Make every if/else and match branch the same type
Section titled “Fix 5: Make every if/else and match branch the same type”error[E0308]: `if` and `else` have incompatible types --> src/main.rs:3:52 |3 | let label = if stock > 0 { "in stock" } else { stock }; | ---------- ^^^^^ expected `&str`, found integer | | | expected because of thisWhen if or match produces a value, Rust needs to know its one type. So every branch must produce the same type. The first branch sets the expectation, and the error points at the branch that disagrees. For match the message says “match arms have incompatible types”. Make the odd branch agree:
fn status_text(code: u16) -> String { match code { 200 => "OK".to_string(), 404 => "Not Found".to_string(), _ => format!("Unknown code {code}"), // every arm is a String }}
fn main() { let stock = 0; let label = if stock > 0 { "in stock" } else { "sold out" }; // both &str println!("{label}");
println!("{}", status_text(404)); println!("{}", status_text(418));}Output:
sold outNot FoundUnknown code 418Here one arm needed format!, which makes a String, so the other arms became String too. See if/else and match.
Where you’ll usually see it
Section titled “Where you’ll usually see it”- Reading user input.
let age: u32 = input.trim();gives “expectedu32, found&str”. Text is not a number. Fix: parse it:let age: u32 = input.trim().parse().unwrap();(or handle the error, see Result). - Pushing literals into a
Vec<String>.tags.push("rust")gives “expectedString, found&str”. Fix:tags.push("rust".to_string()), or make it aVec<&str>if the strings are all literals. - Math with mixed number types.
total + nwheretotalisi64andnisi32gives E0308 (and often E0277 next to it). Fix: convert one side first:total + i64::from(n).
Related errors
Section titled “Related errors”- E0277: trait bound not satisfied: often appears next to E0308, for example when you add an
i64to ani32. - E0599: no method found: you called a method that doesn’t exist on this type, often because the value has a different type than you thought.
- E0425: cannot find value in this scope: a name that Rust doesn’t know at all.
- All compiler errors: the full list of error pages.
Quick check
Section titled “Quick check”Quick check
1. A function says -> i32, but the error says "expected i32, found ()". What's the most likely cause?
2. A function takes &str and you have let name = String::from("Ana");. How do you pass it?
3. Which conversion can never silently lose data?
Score: 0 / 3
Read more
Section titled “Read more”- E0308 — Rust error codes index: the official explanation of this error.
- Functions with Return Values — The Rust Book: why a trailing semicolon changes what a function returns.
- Storing UTF-8 Text with Strings — The Rust Book: how
Stringand&strrelate and how to convert between them. - Conversion — Rust by Example:
From,Into,TryFromandTryIntowith short examples. - Option — Rust standard library: every method for getting a value out of an
Option, likeunwrap_orandmap.