Type coercions are implicit operations that change the type of a value. They happen automatically at specific locations and are highly restricted in what types actually coerce.
Coercions are originally defined in RFC 401 and expanded upon in RFC 1558.
A coercion can only occur at certain coercion sites in a program; these are typically places where the desired type is explicit or can be derived by propagation from explicit types (without type inference). Possible coercion sites are:
-
let
statements where an explicit type is given.For example,
&mut 42
is coerced to have type&i8
in the following:let _: &i8 = &mut 42;
-
static
andconst
item declarations (similar tolet
statements). -
Arguments for function calls
The value being coerced is the actual parameter, and it is coerced to the type of the formal parameter.
For example,
&mut 42
is coerced to have type&i8
in the following:fn bar(_: &i8) { } fn main() { bar(&mut 42); }
For method calls, the receiver (
self
parameter) can only take advantage of unsized coercions. -
Instantiations of struct, union, or enum variant fields
For example,
&mut 42
is coerced to have type&i8
in the following:struct Foo<'a> { x: &'a i8 } fn main() { Foo { x: &mut 42 }; }
-
Function results–either the final line of a block if it is not semicolon-terminated or any expression in a
return
statementFor example,
x
is coerced to have type&dyn Display
in the following:use std::fmt::Display; fn foo(x: &u32) -> &dyn Display { x }
-
The as type cast operator can also explicitly perform type coersion.
If the expression in one of these coercion sites is a coercion-propagating expression, then the relevant sub-expressions in that expression are also coercion sites. Propagation recurses from these new coercion sites. Propagating expressions and their relevant sub-expressions are:
-
Array literals, where the array has type
[U; n]
. Each sub-expression in the array literal is a coercion site for coercion to typeU
. -
Array literals with repeating syntax, where the array has type
[U; n]
. The repeated sub-expression is a coercion site for coercion to typeU
. -
Tuples, where a tuple is a coercion site to type
(U_0, U_1, ..., U_n)
. Each sub-expression is a coercion site to the respective type, e.g. the zeroth sub-expression is a coercion site to typeU_0
. -
Parenthesized sub-expressions (
(e)
): if the expression has typeU
, then the sub-expression is a coercion site toU
. -
Blocks: if a block has type
U
, then the last expression in the block (if it is not semicolon-terminated) is a coercion site toU
. This includes blocks which are part of control flow statements, such asif
/else
, if the block has a known type.
Coercion is allowed between the following types:
-
T
toU
ifT
is a subtype ofU
(reflexive case) -
T_1
toT_3
whereT_1
coerces toT_2
andT_2
coerces toT_3
(transitive case)Note that this is not fully supported yet.
-
&mut T
to&T
-
*mut T
to*const T
-
&T
to*const T
-
&mut T
to*mut T
-
&T
or&mut T
to&U
ifT
implementsDeref<Target = U>
. For example:use std::ops::Deref; struct CharContainer { value: char, } impl Deref for CharContainer { type Target = char; fn deref<'a>(&'a self) -> &'a char { &self.value } } fn foo(arg: &char) {} fn main() { let x = &mut CharContainer { value: 'y' }; foo(x); //&mut CharContainer is coerced to &char. }
-
&mut T
to&mut U
ifT
implementsDerefMut<Target = U>
. -
TyCtor(
T
) to TyCtor(U
), where TyCtor(T
) is one of&T
&mut T
*const T
*mut T
Box<T>
and where
U
can be obtained fromT
by unsized coercion. -
Non capturing closures to
fn
pointers -
!
to anyT
The following coercions are called unsized coercions
, since they
relate to converting sized types to unsized types, and are permitted in a few
cases where other coercions are not, as described above. They can still happen
anywhere else a coercion can occur.
Two traits, Unsize
and CoerceUnsized
, are used
to assist in this process and expose it for library use. The following
coercions are built-ins and, if T
can be coerced to U
with one of them, then
an implementation of Unsize<U>
for T
will be provided:
-
[T; n]
to[T]
. -
T
todyn U
, whenT
implementsU + Sized
-
Foo<..., T, ...>
toFoo<..., U, ...>
, when:Foo
is a struct.T
implementsUnsize<U>
.- The last field of
Foo
has a type involvingT
. - If that field has type
Bar<T>
, thenBar<T>
implementsUnsized<Bar<U>>
. - T is not part of the type of any other fields.
Additionally, a type Foo<T>
can implement CoerceUnsized<Foo<U>>
when T
implements Unsize<U>
or CoerceUnsized<Foo<U>>
. This allows it to provide a
unsized coercion to Foo<U>
.
Note: While the definition of the unsized coercions and their implementation has been stabilized, the traits themselves are not yet stable and therefore can't be used directly in stable Rust.