Conversion and Casting

Both C# and Rust are statically-typed at compile time. Hence, after a variable is declared, assigning a value of a value of a different type (unless it's implicitly convertible to the target type) to the variable is prohibited. There are several ways to convert types in C# that have an equivalent in Rust.

Implicit conversions

Implicit conversions exist in C# as well as in Rust (called type coercions). Consider the following example:

int intNumber = 1;
long longNumber = intNumber;

Rust is much more restrictive with respect to which type coercions are allowed:

let int_number: i32 = 1;
let long_number: i64 = int_number; // error: expected `i64`, found `i32`

An example for a valid implicit conversion using subtyping is:

fn bar<'a>() {
    let s: &'static str = "hi";
    let t: &'a str = s;
}

See also:

Explicit conversions

If converting could cause a loss of information, C# requires explicit conversions using a casting expression:

double a = 1.2;
int b = (int)a;

Explicit conversions can potentially fail at run-time with exceptions like OverflowException or InvalidCastException when down-casting.

Rust does not provide coercion between primitive types, but instead uses explicit conversion using the as keyword (casting). Casting in Rust will not cause a panic.

let int_number: i32 = 1;
let long_number: i64 = int_number as _;

Custom conversion

Commonly, .NET types provide user-defined conversion operators to convert one type to another type. Also, System.IConvertible serves the purpose of converting one type into another.

In Rust, the standard library contains an abstraction for converting a value into a different type, in form of the From trait and its reciprocal, Into. When implementing From for a type, a default implementation for Into is automatically provided (called blanket implementation in Rust). The following example illustrates two of such type conversions:

fn main() {
    let my_id = MyId("id".into()); // `into()` is implemented automatically due to the `From<&str>` trait implementation for `String`.
    println!("{}", String::from(my_id)); // This uses the `From<MyId>` implementation for `String`.
}

struct MyId(String);

impl From<MyId> for String {
    fn from(MyId(value): MyId) -> Self {
        value
    }
}

See also: