Type System Fundamentals
Type Coercions
A type coercion is an implicit, lossless type conversion that the compiler performs automatically at specific coercion sites (such as let bindings with explicit types, function arguments, struct field initializers, and return expressions). Unlike C, Rust never performs implicit numeric coercions (i32 to i64 or f64).
The Six Implicit Coercions in Safe Rust
| Coercion Kind | Source Type $\rightarrow$ Target Type | Example |
|---|---|---|
| 1. Deref coercion | &T $\rightarrow$ &U (where T: Deref<Target = U>) |
&String $\rightarrow$ &str, &Vec<T> $\rightarrow$ &[T], &Box<T> $\rightarrow$ &T |
| 2. Unsize coercion | &[T; N] $\rightarrow$ &[T], or &Concrete $\rightarrow$ &dyn Trait |
&[1, 2, 3] $\rightarrow$ &[i32], Box<Circle> $\rightarrow$ Box<dyn Draw> |
| 3. Mutability weakening | &mut T $\rightarrow$ &T, or *mut T $\rightarrow$ *const T |
Passing &mut buf to a function expecting &buf |
| 4. Reference-to-raw-pointer | &T $\rightarrow$ *const T, or &mut T $\rightarrow$ *mut T |
Passing &val to a *const T parameter |
5. Non-capturing closure / fn item to fn pointer |
fn foo() or |x| x + 1 $\rightarrow$ fn(i32) -> i32 |
Storing closures/functions in a Vec<fn(i32) -> i32> |
6. Never type (!) coercion |
! $\rightarrow$ any type T |
return, break, continue, panic!, or loop {} inside an if / else arm |
Demonstrating the Implicit Coercions Together
fn accept_slice(s: &[i32]) -> i32 {
s.iter().sum()
}
fn main() {
// 1. Unsize coercion: `&[i32; 3]` -> `&[i32]`
let arr = [10, 20, 30];
println!("Array -> slice sum: {}", accept_slice(&arr));
// 2. Mutability weakening + Deref coercion: `&mut Vec<i32>` -> `&[i32]`
let mut vec = vec![5, 15];
println!("&mut Vec -> slice sum: {}", accept_slice(&mut vec));
// 3. Non-capturing closures coercing to a common function pointer `fn(i32) -> i32`:
let ops: [fn(i32) -> i32; 2] = [|x| x + 1, |x| x * 10];
println!("ops[0](4) = {}, ops[1](4) = {}", ops[0](4), ops[1](4));
// 4. Never type `!` coercing to `i32`:
let flag = true;
let val: i32 = if flag { 42 } else { panic!("unreachable") };
println!("val = {val}");
}
Array -> slice sum: 60
&mut Vec -> slice sum: 20
ops[0](4) = 5, ops[1](4) = 40
val = 42
Implicit Coercion vs. Explicit as Cast vs. From / Into
| Mechanism | Syntax | Applies To | Can Lose Information? |
|---|---|---|---|
| Implicit coercion | Automatic at coercion sites | References, pointers, unsizing, fn pointers, ! |
No |
From / Into |
u64::from(x_u32) / x.into() |
Value-preserving conversions between distinct types | No (infallible and lossless) |
TryFrom / TryInto |
u8::try_from(x_i32) |
Narrowing or validated conversions | Returns Err if out of range |
as cast |
x as u8, ptr as usize |
Primitive scalars, raw pointers, C-like enums | Yes (truncates integers, saturates float-to-int) |