The Type System & Inference
Rust is statically typed: every value has a type known at compile time. The compiler infers types when possible; annotations clarify intent at API boundaries and resolve ambiguity.
Busca en todas las páginas de la documentación
Rust is statically typed: every value has a type known at compile time. The compiler infers types when possible; annotations clarify intent at API boundaries and resolve ambiguity.
fn main() {
let x = 10; // inferred i32
let y: u64 = 10; // explicit
let items = vec![1, 2, 3]; // Vec<i32>
let mapped: Vec<_> = items.iter().map(|n| n * 2).collect();
println!("{x} {y} {:?}", mapped);
}When to reach for this: Choosing explicit types at public APIs, turbofish ::<T>, and understanding why inference sometimes needs a hint.
use std::io::{self, BufRead};
fn read_numbers<R: BufRead>(reader: R) -> io::Result<Vec<i32>> {
let mut out = Vec::new();
for line in reader.lines() {
let n: i32 = line?.trim().parse()?;
out.push(n);
}
Ok(out)
}
fn main() -> io::Result<()> {
let stdin = io::stdin();
let nums = read_numbers(stdin.lock())?;
let sum: i32 = nums.iter().sum();
println!("sum={sum}");
Ok(())
}What this demonstrates:
R: BufRead inferred at call site? on parse() propagates ParseIntError via Fromi32 on parse targetio::Result<Vec<i32>> guides error conversionsi32, floats to f64 without other hintscollect() often needs turbofish: collect::<Vec<_>>()let v = Vec::<String>::new();
let v: Vec<String> = Vec::new();
let nums = "1 2 3".split_whitespace().map(|s| s.parse::<u32>().unwrap()).collect::<Vec<_>>();!fn exit(code: i32) -> ! {
std::process::exit(code);
}Expressions of type ! unify with any type in match arms (diverging).
collect() - let v = iter.collect(); errors. Fix: collect::<Vec<_>>() or annotate let v: Vec<T>.let x: T = 1; may fail. Fix: Use T::from(1) or literal suffix.usize == u64 - Platform-dependent usize. Fix: Cast explicitly for FFI/serialization.let x = 1; let x = "a"; changes type silently. Fix: Use distinct names in long scopes.
| Alternative | Use When | Don't Use When |
|---|---|---|
Turbofish ::<T> | One-off method type param | Repeated - use type alias |
Type alias type Id = u64 | Domain clarity | Single-use primitives |
impl Trait in returns | Hide concrete type | Callers need named type |
| Newtype wrapper | Distinct units | Internal-only math |
No in safe application code. dyn Trait is still statically checked at compile time for trait object usage.
Sometimes. Annotate the return type on recursive functions explicitly.
i32 for untyped integer literals in most contexts.
The expression is still type-checked. _ discards the binding but not the type constraints.
Closure param/return types are inferred from usage. Annotate with |x: u32| -> u32 when needed.
Another name for explicit annotations: let x: i32 = 1.
Not directly. Use std::any::type_name::<T>() at runtime for debugging.
No. type Meters = f64 is interchangeable with f64. Use newtype struct for distinction.
Future output types are inferred from .await usage and return position in async fns.
rust-analyzer shows inferred types on hover. cargo check is the source of truth.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Revisado por Chris St. John·Última actualización: 19 jul 2026