1. Symptoms
When the Rust compiler encounters error E0045, you will see a message indicating a type mismatch during function invocation or generic resolution. The error manifests with the compiler explicitly stating that an expected type was not found in the provided arguments. Common presentation includes the file location, line number, and a clear comparison between the expected parameter type and the actual type passed by the caller.
Shell output typically shows something similar to the following pattern:
error[E0045]: type mismatch: the type has an expected type
--> src/main.rs:5:13
|
5 | some_function(argument);
| ^^^^^^^^ expected i32, found &str
|
= note: expected type `i32`
found type `&str`
The compiler may also present the error in a more verbose format when dealing with generic functions:
error[E0045]: type mismatch resolving `<fn(&str) -> () as FnOnce<(&i32,)>>::Output`
--> src/main.rs:7:5
|
7 | (closure)(42);
| ^^ expected &str, found i32
In more complex scenarios involving trait objects or dynamically dispatched code, the error message may reference the trait resolution process:
error[E0045]: mismatched types
--> src/main.rs:10:20
|
10 | let result: Box<dyn Fn(String)> = Box::new(|x: i32| x.to_string());
| ^^^^^^^^^^^^^^^^^ expected struct `String`, found `i32`
2. Root Cause
Error E0045 originates from Rust’s strict type system, which enforces that function arguments must exactly match the declared parameter types. The compiler performs type checking at the point of function call, comparing the static types of provided arguments against the expected types of function parameters. When these types are incompatible, the compiler cannot unify them and emits E0045.
Several scenarios commonly trigger this error. The most frequent cause is passing an argument of the wrong fundamental type, such as providing a string literal when an integer is expected. Rust performs no implicit type conversions between unrelated types, so some_function("42") will not compile if some_function expects an i32.
Another common trigger involves lifetime mismatches in references. When a function expects a reference with a specific lifetime, providing a reference with a shorter or unrelated lifetime causes this error. For example, a function signature fn process(data: &str) expects a borrowing reference, but passing an owned String directly without borrowing would not match.
Generic function calls can also produce E0045 when type inference fails to resolve the correct concrete types, or when the provided argument types cannot satisfy the generic constraints declared on the function. The compiler attempts to unify the argument types with the parameter types, but when this unification fails due to fundamental incompatibilities, E0045 results.
Closely related to this is the case of closure type mismatches. When passing closures as function arguments, the closure’s captured environment and parameter types must align with the function signature. A closure that captures environment by move may have a different type than expected, causing the mismatch.
3. Step-by-Step Fix
Addressing E0045 requires identifying the type mismatch and either converting the argument to the expected type or adjusting the function signature to accept the provided type. The following approaches cover the most common resolution strategies.
Fix 1: Explicit Type Conversion
When the argument type is convertible to the expected type, perform an explicit conversion using methods like into(), to_string(), or type casting syntax.
Before:
fn print_number(value: i32) {
println!("Number: {}", value);
}
fn main() {
let text = "42";
print_number(text); // E0045: expected i32, found &str
}
After:
fn print_number(value: i32) {
println!("Number: {}", value);
}
fn main() {
let text = "42";
print_number(text.parse::<i32>().unwrap()); // Correctly converts &str to i32
}
Fix 2: Borrowing Correctly
When a function expects a reference but you are passing an owned value, you need to borrow explicitly using the & operator.
Before:
fn process_name(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
let owned_string = String::from("Alice");
process_name(owned_string); // E0045: expected &str, found String
}
After:
fn process_name(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
let owned_string = String::from("Alice");
process_name(&owned_string); // Correctly borrows the String as &str
}
Fix 3: Adjusting Function Signatures
When the caller provides a reasonable type that the function should accept, modify the function signature to accept the provided argument type.
Before:
fn calculate(value: i64) -> i64 {
value * 2
}
fn main() {
let small: i32 = 10;
let result = calculate(small); // E0045: expected i64, found i32
}
After:
fn calculate<T: Into<i64>>(value: T) -> i64 {
value.into() * 2
}
fn main() {
let small: i32 = 10;
let result = calculate(small); // Now accepts any type that converts to i64
}
Fix 4: Matching Closure Types
When passing closures to higher-order functions, ensure the closure signature exactly matches the expected function signature.
Before:
fn apply<F>(func: F, val: i32) -> i32
where
F: Fn(i32) -> String,
{
func(val).parse().unwrap()
}
fn main() {
let result = apply(|x| x, 42); // E0045: closure returns i32, expected String
}
After:
fn apply<F>(func: F, val: i32) -> i32
where
F: Fn(i32) -> i32,
{
func(val)
}
fn main() {
let result = apply(|x| x, 42); // Correctly matches: closure takes i32, returns i32
}
4. Verification
After applying a fix, verify that the code compiles successfully by running cargo build or cargo check. A successful build produces no error output, confirming that the type mismatch has been resolved.
$ cargo build
Compiling my_project v0.1.0 (file:///path/to/project)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
Run the resulting binary to ensure the program executes correctly with the fixed code:
$ cargo run
Hello, Alice!
Number: 42
Result: 84
For more complex scenarios involving generic functions or trait objects, add type annotations to confirm the compiler resolves the correct types:
fn main() {
let result: i32 = calculate(42);
println!("Result: {}", result);
}
If the code still produces E0045, carefully re-examine the error message pointing to the exact location and compare the expected and found types mentioned in the compiler output. Double-check that all conversions are complete and that borrowing is applied consistently throughout the call chain.
5. Common Pitfalls
One frequent mistake is assuming Rust performs implicit numeric conversions between different integer types. Unlike some languages, Rust requires explicit casting between numeric types using as expressions or conversion methods like i32::from().
Another pitfall involves confusing reference types with their referent types. Passing String where &str is expected fails because &str is a borrowed string slice, not an owned string. You must borrow owned values explicitly.
When working with closures, remember that each closure expression creates a unique anonymous type, even if the signatures match. Two closures with identical signatures are still considered different types at the type system level. Always use trait bounds like Fn or FnOnce in function signatures when accepting closures as parameters.
Forgetting to borrow in the correct scope leads to lifetime-related mismatches. In nested function calls, ensure each layer borrows correctly, particularly when passing references through multiple function calls. Dropping a reference before the function receives it causes subtle lifetime errors that may manifest as E0045.
When using generic functions with multiple type parameters, ensure each type argument satisfies all constraints declared on the corresponding type parameter. A common error is providing a type that satisfies one constraint but violates another, leading to confusing error messages about mismatched types.
6. Related Errors
E0308: Mismatched Types is the most closely related error and frequently appears alongside or instead of E0045 in type mismatch scenarios. While E0045 often appears in more specific contexts like closure type resolution, E0308 covers the broader category of type mismatches in assignment, return statements, and general expressions.
E0067: Invalid left-hand side of assignment occurs when attempting to assign to an expression that cannot be assigned to, sometimes confused with type mismatch errors when dealing with assignment syntax.
E0050: Cannot provide explicit generic arguments for Fn family traits’ function relates to higher-order function calls where type inference complexity may cause confusion about expected argument types.