CodeBlanks Logo Icon CodeBlanks

Type inference

Learn the Hindley-Milner style type inference algorithm.

Type inference

Rust's type inference works within a function body by collecting constraints and unification: each expression of unknown type gets a type variable, uses create equality constraints between these variables, and the solver solves them all together. That's why information can come "later":

let a = 5;          // a: ?T (integer)
let b: u16 = a;     // constraint: ?T = u16 → a: u16

let mut v = Vec::new();   // Vec<?U>
v.push('ç');               // ?U = char

Inference doesn't cross function boundaries: signatures are always written explicitly. This keeps error messages local and APIs stable.

Observing inference

Practical ways to see the inferred type: IDE hints (rust-analyzer), making a deliberate type error (let () = x; makes the compiler state the type), or looking at the size:

let a = 5;
let _b: u16 = a;
println!("{}", std::mem::size_of_val(&a));    // 2: a was inferred as u16

let mut v = Vec::new();
v.push('ç');
println!("{}", std::mem::size_of_val(&v[0])); // 4: char

Practice Information that comes later

Declare let a = 5; and assign it to a variable of type u16. Push 'ç' into an empty Vec::new(). Print the results of size_of_val(&a) and size_of_val(&v[0]) separated by a space.

Starter code

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
fn main() {
    let a = 5;
    let _b: u16 = a;
    let mut v = Vec::new();
    v.push('ç');
    println!("{} {}", std::mem::size_of_val(&a), std::mem::size_of_val(&v[0]));
}

Expected output

2 4

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 With no constraints, what type does <code>let x = 5;</code> get?

  • i32 (the integer default) Correct answer
  • i64
  • usize
  • Compile error

Explanation: The default is i32 for integer literals and f64 for floats; it applies only when there are no other constraints.

2 Size of the value

Question
let a = 5;
let _b: u16 = a;
println!("{}", std::mem::____(&a));   // 2
Answer
let a = 5;
let _b: u16 = a;
println!("{}", std::mem::size_of_val(&a));   // 2

Explanation: size_of_val gives a value's size in bytes.

3 Code and inferred type

Term Meaning
let x = 5; i32
let y = 2.0; f64
let a = 5; let b: u16 = a; a: u16
Vec::new() + push('ç') Vec<char>

All lessons in Expert