16.3 Safe, Infallible Conversions: From and Into

The From<T> and Into<U> traits represent conversions that are guaranteed to succeed, meaning they are infallible. This contrasts with conversions that might fail, such as narrowing an integer (i32 to i8), which are handled by the TryFrom/TryInto traits (covered in Section 16.4). From/Into are the idiomatic Rust way to express a safe and unambiguous transformation from one type to another. Crucially, for conversions where success cannot be guaranteed (like i32 to u8), the From trait is deliberately not implemented in the standard library. This forces the programmer to choose an alternative: either the safe, error-handling approach with TryFrom, or an explicit, potentially lossy cast using as.

  • impl From<T> for U defines how to create a U instance from a T instance.
  • If From<T> is implemented for U, the compiler automatically provides an implementation of Into<U> for T. This works because the standard library includes a generic implementation conceptually similar to impl<T, U> Into<U> for T where U: From<T> { fn into(self) -> U { U::from(self) } }. Essentially, calling .into() on a value of type T delegates the conversion to the U::from(t) implementation.

Conversion can be invoked via U::from(value_t) or value_t.into(). The into() method relies on type inference; the compiler must be able to determine the target type U from the context (e.g., variable type annotation).

16.3.1 Standard Library Examples

The standard library provides numerous From implementations for common, safe conversions:

fn main() {
    // Integer widening (always safe)
    let val_u8: u8 = 100;
    let val_i32 = i32::from(val_u8); // Explicit call to from()
    let val_u16: u16 = val_u8.into();
    // into() infers target type from variable declaration
    println!("u8: {}, converted to i32: {}, converted to u16: {}",
             val_u8, val_i32, val_u16);

    // String conversions
    let message_slice = "Hello from slice";
    let message_string = String::from(message_slice);
    // Canonical way to create owned String from &str
    let message_string_again: String = message_slice.into();
    // Also works due to From<&str> for String
    println!("Owned string: {}", message_string);
    println!("Owned string (via into): {}", message_string_again);

    // Creating collections
    // Here, [1, 2, 3] is an array literal of type [i32; 3]
    let vec_from_array = Vec::from([1, 2, 3]);
    // Convert the Vec<i32> into an owned slice Box<[i32]>
    // Vec<T> can be converted into Box<[T]> and vice versa via From/Into.
    // Note: [i32] is a dynamically sized slice type; Box<[i32]> owns it.
    let boxed_slice: Box<[i32]> = vec_from_array.into();
    println!("Boxed slice: {:?}", boxed_slice);
}

16.3.2 Implementing From for Custom Types

Implement From to define standard, safe conversions for your own data structures:

#[derive(Debug)]
struct Point3D {
    x: i64,
    y: i64,
    z: i64,
}

// Allow creating a Point3D from a tuple (i64, i64, i64)
impl From<(i64, i64, i64)> for Point3D {
    fn from(tuple: (i64, i64, i64)) -> Self {
        Point3D { x: tuple.0, y: tuple.1, z: tuple.2 }
    }
}

// Allow creating a Point3D from an array [i64; 3]
impl From<[i64; 3]> for Point3D {
    fn from(arr: [i64; 3]) -> Self {
        Point3D { x: arr[0], y: arr[1], z: arr[2] }
    }
}

fn main() {
    let p1 = Point3D::from((10, -20, 30));
    let p2: Point3D = [40, 50, 60].into(); // Type inference works here

    println!("p1: {:?}", p1);
    println!("p2: {:?}", p2);
}

Using From/Into clearly signals that the conversion is a standard, safe, and lossless transformation for the involved types.