16.3 Using the From and Into Traits

The From and Into traits provide a safe and idiomatic way to perform type conversions in Rust. They are widely used in the standard library and can be implemented for custom types.

The From trait allows a type to define how to create itself from another type, while Into is automatically implemented for any type that implements From.

16.3.1 Standard Library Examples

The From and Into traits are defined for most data types in the standard library and are restricted to safe operations for primitive types.

Example:

fn main() {
    let x: i32 = i32::from(10u16); // From<u16> for i32
    let y: i32 = 10u16.into();     // Into<i32> for u16
    println!("x: {}, y: {}", x, y);

    let my_str = "hello";
    let my_string = String::from(my_str);
    println!("{}", my_string);
}

16.3.2 Implementing From and Into for Custom Types

Custom types can implement From and Into to define their own conversions.

Example:

#[derive(Debug)]
struct MyNumber(i32);

impl From<i32> for MyNumber {
    fn from(item: i32) -> Self {
        MyNumber(item)
    }
}

fn main() {
    let num = MyNumber::from(42);
    println!("{:?}", num);

    let num: MyNumber = 42.into();
    println!("{:?}", num);
}

In this example:

  • We implement From<i32> for MyNumber, allowing us to create a MyNumber from an i32.
  • Since Into<MyNumber> is automatically implemented for i32, we can use .into() to perform the conversion.

16.3.3 Using as and Into for Function Parameters

When calling functions, it can be necessary to convert parameters. The use of into() has the advantage of better type safety, and the destination type is automatically inferred.

Example:

fn test(x: f64) {
    println!("{}", x);
}

fn main() {
    let i = 1;
    test(i as f64);
    test(i as _);
    test(i.into());
}

In this example:

  • The as keyword explicitly casts i to f64 or uses type inference.
  • The into() method converts i to f64 by leveraging the Into trait, and the type is inferred.

16.3.4 Performance Comparison of as and Into

For primitive types, conversions with Into and From are optimized by the compiler and typically have the same performance as as. However, Into provides a more type-safe and extensible approach.