Types can implicitly be coerced to change in certain contexts.
These changes are generally just weakening of types, largely focused around pointers and lifetimes.
They mostly exist to make Rust "just work" in more cases, and are largely harmless.
There are four possible coercions categories in Rust:
If T
coerces to U
and U
coerces to V
, then T
coerces to V
.
mut T
to T
, &mut T
to &T
and *mut T
to *const T
.&mut T
to *mut T
and &T
to *const T
.If T: Deref<Target = U>
, then &T coerces to &U
via the deref()
method.
(Similarly, if T: DerefMut
, then &mut T
coerces to &mut U
via deref_mut()
)
Types that can be "unsized" to a dynamically-sized type.
If Ptr
is a "pointer type" (e.g. &T
, *mut T
, Box
, Rc
, etc), and
T: Unsize<U>
, then Ptr<T>
coerces to Ptr<U>
.
The Unsize
trait is automatically implemented for:
[T; n]
: Unsize<[T]>
T: Unsize<Trait>
where T: Trait
struct Foo<...> { ... , field: T }: Unsize< struct Foo<...> { ..., field: U }>
,
provided that T: Unsize<U>
(and some more conditions to make the job easier
for the compiler).Unsize
is used along with ops::CoerceUnsized
to allow "user-defined"
containers such as Rc
to contain dynamically-sized types.
Rust recognizes Ptr<X>
as a "pointer type" if it implements CoerceUnsized
.
The actual rule is stated as, "if T: CoerceUnsized<U>
then T
coerces to U
".)
All implementations of Unsize
are provided automatically by the compiler.
The reason &[T; n]
coerces to &[T]
is rule 4:
impl Unsize<[T]>
for [T; n]
for
every [T; n]
, and&X
is a pointer type.Using these, &[T; n]
can coerce to &[T]
.
Note that coercion is not performed when matching traits.
If there is an implementation for some type U
and T
coerces to U
, that
does not constitute an implementation for T
. For example, the following will
not type check, even though it is ok to coerce it to &T
and there is an
implementation for &T
:
trait Trait {}
fn foo<X: Trait>(t: X) {}
impl<'a> Trait for &'a i32 {}
fn main() {
let t: &mut i32 = &mut 0;
foo(t);
}
Which fails like as follows:
error[E0277]: the trait bound `&mut i32: Trait` is not satisfied
--> src/main.rs:9:9
|
3 | fn foo<X: Trait>(t: X) {}
| ----- required by this bound in `foo`
...
9 | foo(t);
| ^ the trait `Trait` is not implemented for `&mut i32`
|
= help: the following implementations were found:
<&'a i32 as Trait>
= note: `Trait` is implemented for `&i32`, but not for `&mut i32`
Proudly self-hosted on a cheap Raspberry Pi