Type coercions - The Rust Reference (original) (raw)

The Rust Reference

Type coercions

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.

Any conversions allowed by coercion can also be explicitly performed by thetype cast operator, as.

Coercions are originally defined in RFC 401 and expanded upon in RFC 1558.

Coercion sites

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:

#![allow(unused)]  
fn main() {  
let _: &i8 = &mut 42;  
}  
fn bar(_: &i8) { }  
fn main() {  
    bar(&mut 42);  
}  

For method calls, the receiver (self parameter) type is coerced differently, see the documentation on method-call expressions for details.

struct Foo<'a> { x: &'a i8 }  
fn main() {  
    Foo { x: &mut 42 };  
}  
#![allow(unused)]  
fn main() {  
use std::fmt::Display;  
fn foo(x: &u32) -> &dyn Display {  
    x  
}  
}  

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:

Coercion types

Coercion is allowed between the following types:

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.  
}  

Unsized Coercions

The following coercions are called unsized coercions, since they relate to converting 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:

Additionally, a type Foo<T> can implement CoerceUnsized<Foo<U>> when Timplements Unsize<U> or CoerceUnsized<Foo<U>>. This allows it to provide an 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.

Least upper bound coercions

In some contexts, the compiler must coerce together multiple types to try and find the most general type. This is called a “Least Upper Bound” coercion. LUB coercion is used and only used in the following situations:

In each such case, there are a set of types T0..Tn to be mutually coerced to some target type T_t, which is unknown to start.

Computing the LUB coercion is done iteratively. The target type T_t begins as the type T0. For each new type Ti, we consider whether

Examples:

#![allow(unused)]
fn main() {
let (a, b, c) = (0, 1, 2);
// For if branches
let bar = if true {
    a
} else if false {
    b
} else {
    c
};

// For match arms
let baw = match 42 {
    0 => a,
    1 => b,
    _ => c,
};

// For array elements
let bax = [a, b, c];

// For closure with multiple return statements
let clo = || {
    if true {
        a
    } else if false {
        b
    } else {
        c
    }
};
let baz = clo();

// For type checking of function with multiple return statements
fn foo() -> i32 {
    let (a, b, c) = (0, 1, 2);
    match 42 {
        0 => a,
        1 => b,
        _ => c,
    }
}
}

In these examples, types of the ba* are found by LUB coercion. And the compiler checks whether LUB coercion result of a, b, c is i32 in the processing of the function foo.

Caveat

This description is obviously informal. Making it more precise is expected to proceed as part of a general effort to specify the Rust type checker more precisely.