← Back to Rust Course | Chapter 2: Variables & Types | Lesson 7 of 7

Type Inference

Rust is smart enough to figure out what kind of value you meant most of the time, so you often don't need to say it yourself.

Inference from Literals

When you write let x = 5;, Rust looks at the literal and defaults it to i32 without you writing the type. The compiler performs this inference at compile time -- there is no runtime type-checking involved.

Example: Inference from Literals

markup
fn main() {
    let count = 5;
    let name = "Rustacean";
    println!("{} appears {} times", name, count);
}

Inference from Later Usage

Rust can also infer a variable's type from how it is used later in the code, not just from its initial value, as long as the usage is unambiguous.

Note: Here Rust infers Vec<i32> only after seeing the first push(1) call.

Example: Inference from Later Usage

markup
fn main() {
    let mut numbers = Vec::new();
    numbers.push(1);
    numbers.push(2);
    println!("{:?}", numbers);
}

Function Signatures Need Explicit Types

Unlike local variables, function parameters and return types are never inferred -- they must always be written out explicitly. This makes function signatures a reliable, self-documenting contract.

Example: Function Signatures Need Explicit Types

markup
fn double(n: i32) -> i32 {
    n * 2
}

fn main() {
    println!("Doubled: {}", double(21));
}

When Inference Fails

Sometimes the compiler cannot determine a type on its own, such as when a generic parsing method could produce several different types. In that case you must supply an explicit annotation to resolve the ambiguity.

Example: When Inference Fails

markup
fn main() {
    let parsed: i32 = "42".parse().unwrap();
    println!("Parsed as i32: {}", parsed);
}
Common Mistakes
  1. Believing Rust is dynamically typed because you rarely write type annotations -- every value still has one fixed type, just inferred.
  2. Leaving out a type annotation in a case where the compiler genuinely cannot infer it, leading to a compile error.
  3. Assuming inference works across function boundaries -- function parameters and return types must always be explicitly annotated.
Chapter Summary
  • Rust infers types from context, such as literal values, later usage, or return values.
  • Every value still has exactly one concrete type at compile time -- inference does not make Rust dynamically typed.
  • Function signatures always require explicit parameter and return types, even though local variables often don't.
  • When inference is ambiguous, the compiler reports an error asking for an explicit annotation.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.