Conversion Traits

Conversion traits like From↗, Into, TryFrom, and TryInto enable type-safe transformations between types. From is the most common trait for defining conversions - it is implemented on the destination type and lets you create an instance from another type. Into is automatically implemented when From is, allowing values to be converted with .into(). For fallible conversions,TryFrom and TryInto return a Result, adding error handling when the conversion might fail.

Convert between Types with From

std

The From trait defines how one type can be turned into another. Implementing From<T> for the target type allow you to write conversions like let x: Target = Target::from(source);.

Since From is infallible by design, conversions must never fail. From enables automatic Into implementations, so types implementing From can be seamlessly used with .into():

#[derive(Debug)]
struct Millimeters(u32);

#[derive(Debug)]
struct Meters(u32);

impl From<Meters> for Millimeters {
    fn from(m: Meters) -> Self {
        Millimeters(m.0 * 1000)
    }
}

fn main() {
    let m = Meters(2);
    let mm: Millimeters = m.into();
    println!("{mm:?}");
    assert_eq!(mm.0, 2000);
    // OR
    let m = Meters(4);
    let mm = Millimeters::from(m);
    println!("{mm:?}");
    assert_eq!(mm.0, 4000);
}

From is often used to convert custom errors. You may also use the anyhow and thiserror crates.

//! Converting Custom Errors.
use std::io;

#[derive(Debug)]
enum MyError {
    Io(io::Error),
    Message(String),
}

impl From<io::Error> for MyError {
    fn from(err: io::Error) -> Self {
        MyError::Io(err)
    }
}

impl From<&str> for MyError {
    fn from(msg: &str) -> Self {
        MyError::Message(msg.to_string())
    }
}

fn main() -> Result<(), MyError> {
    let _file = std::fs::File::open("missing.txt")?; // `io::Error` becomes `MyError`.
    Err("custom failure")?; // `&str` becomes `MyError`.
    Ok(())
}

From may also be used when parsing. To parse strings, implement std::str::FromStr↗ instead.

//! Parsing from raw bytes.

#[derive(Debug)]
struct Message(String);

// Implement `From` to convert from a `u8` slice to our custom struct.
impl From<&[u8]> for Message {
    fn from(bytes: &[u8]) -> Self {
        Message(String::from_utf8_lossy(bytes).into())
    }
}

fn main() {
    let raw: &[u8] = b"Hello world";
    let msg: Message = raw.into(); // Use `From<&[u8]>`.
    println!("{msg:?}");
}

Choose the Right Trait for a Conversion

std

Paraphrasing the std::convert↗ module documentation, you should:

  • Implement the From↗ trait for explicit, infallible, consuming value-to-value conversions.
    • From creates new owned values and automatically implements Into. From is the only trait here that changes ownership and produces a new value, while the others below borrow existing ones.
    • Implement the Into trait for consuming value-to-value conversions to types outside the current crate.
  • Use TryFrom/TryInto when a conversion might fail. The TryFrom and TryInto traits behave like From and Into otherwise.
  • Use AsRef / AsMut when passing around references without consuming. AsRef lends a borrowed view into another type without transferring ownership.
    • Implement the AsRef↗ trait for explicit, cheap reference-to-reference conversions.
    • Implement the AsMut↗ trait for explicit, cheap mutable-reference-to-mutable-reference conversions.

Distinguish the above traits from the following:

  • FromStr↗ provides string parsing with idiomatic error handling, and it's typically implemented for types like u32, Url, and enums. It powers .parse::<T>(). It can only parse types that do not contain a lifetime parameter.
  • Deref↗ provides implicit access to the inner data of smart pointers via dereferencing (*, . method calls) - e.g., from Box<T> to T. It is a key part of Rust's deref coercion system.
  • Borrow↗ enables structural borrowing, especially for collections. Borrow is most often used in standard library collections like HashMap to let you use a borrowed key (e.g., &str) to look up an owned key (String). Unlike AsRef, which is for conversion, Borrow implies full equivalence in behavior. In particular, Eq, Ord and Hash must be equivalent for borrowed and owned values.
  • AsRef.
  • Borrow.
  • Smart Pointers.
  • String Parsing.
  • Traits.