Type Coercions
A type coercion is an implicit, compiler-inserted conversion from one type to another at specific syntactic locations (coercion sites) such as let bindings with explicit type annotations, function arguments, struct field initializers, and return expressions. Unlike From/Into conversions, type coercions are built into the language, never fail, and never allocate memory.
Complete Catalog of Implicit Coercions in Rust
| Coercion Category | Source Type $\rightarrow$ Target Type | Underlying Mechanism |
|---|---|---|
| 1. Reflexive / Subtype coercion | &'long T $\rightarrow$ &'short T (where 'long: 'short) |
Lifetime subtyping & variance |
| 2. Mutability downgrade | &'a mut T $\rightarrow$ &'a T, *mut T $\rightarrow$ *const T |
Drops write permission |
| 3. Reference-to-raw-pointer | &T $\rightarrow$ *const T, &mut T $\rightarrow$ *mut T |
Strips compile-time lifetime & reference guarantees |
| 4. Deref coercion | &T $\rightarrow$ &U (where T: Deref<Target = U>) |
Calls Deref::deref (or DerefMut::deref_mut for &mut) |
5. Unsizing coercion (CoerceUnsized) |
&[T; N] $\rightarrow$ &[T], Box<Concrete> $\rightarrow$ Box<dyn Trait> |
Attaches slice length or vtable metadata to create a fat pointer |
| 6. Trait object upcasting | &dyn Subtrait $\rightarrow$ &dyn Supertrait |
Adjusts vtable pointer to supertrait vtable |
7. Non-capturing closure to fn |
|x: i32| x + 1 $\rightarrow$ fn(i32) -> i32 |
Compiles stateless closure to a plain function pointer |
| 8. Never-type coercion | ! $\rightarrow$ any type T |
Diverging expression never produces a value |
| 9. Least-Upper-Bound (LUB) coercion | Multiple match / if-else arms coerce to a common target type |
Unifies (&[u8; 3], &[u8; 5]) arms into &[u8] when target is &[u8] |
Transitive Chaining of Coercions
The compiler can chain coercions—most notably combining mutability downgrade + deref coercion + unsizing in a single expression:
&mut Box<[u32; 3]>
|
| 1. Deref coercion (`Box<T>: Deref<Target = T>`)
v
&[u32; 3]
|
| 2. Unsize coercion (`[u32; 3]` -> `[u32]`)
v
&[u32]
fn print_slice(slice: &[u32]) {
println!("slice: {:?}", slice);
}
fn main() {
let mut boxed_array: Box<[u32; 3]> = Box::new([10, 20, 30]);
// 1. Mutability downgrade + Deref + Unsizing in a single argument position:
print_slice(&mut boxed_array);
// 2. Non-capturing closure coerced to `fn(i32) -> i32`:
let math_fn: fn(i32) -> i32 = |x| x * 2;
println!("math_fn(21) = {}", math_fn(21));
// 3. Least-Upper-Bound (LUB) coercion across `if`/`else` branches of different array sizes:
let flag = true;
let chosen: &[u8] = if flag { &[1, 2] } else { &[3, 4, 5, 6] };
println!("chosen slice: {:?}", chosen);
}
slice: [10, 20, 30]
math_fn(21) = 42
chosen slice: [1, 2]
Where Coercions Do NOT Happen Automatically
Coercions do not occur on generic type arguments when a trait bound must be satisfied first! For example, if a generic function has signature fn check<T>(a: T, b: T), passing &[1, 2] (&[i32; 2]) and &[3, 4, 5] (&[i32; 3]) fails to unify T unless at least one argument is coerced to &[i32] (&[1, 2][..]) or check::<&[i32]> is specified.