16.3 Using the From and Into Traits

The From and Into traits provide a more structured and idiomatic approach to conversions. Defining a From<T> for type U automatically gives you an Into<U> for type T. These traits make your intent crystal clear and support both built-in and user-defined types.

16.3.1 Standard Library Examples

Many trivial conversions come from the standard library’s implementations of From and Into:

fn main() {
    let x: i32 = i32::from(10u16); 
    let y: i32 = 10u16.into();     
    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

For custom types, implementing From often makes conversion logic simpler and more idiomatic:

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

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

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

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

16.3.3 Using as and Into in Function Calls

Sometimes you need to match the parameter type of a function. You can choose as or Into to perform the conversion:

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

fn main() {
    let i = 1;
    print_float(i as f64);
    print_float(i as _);      // infers f64
    print_float(i.into());    // also infers f64
}

16.3.4 Performance Comparison: as vs. Into

For straightforward numeric conversions, there is no practical performance difference between as and Into. The Rust compiler typically optimizes both paths well. However, From/Into tends to make code more expressive and extensible.