🎓 A1Learner
🔍

Rust TryFrom & TryInto

TryFrom and TryInto are conversion traits for conversions that can fail. A conversion returns a Result instead of a plain value.

A conversion that returns a Result

TryFrom is like From, but the conversion may fail, so it returns a Result. The standard library uses it for narrowing number changes: 300 is too big for a u8.

println!("{:?}", u8::try_from(200));
println!("{:?}", u8::try_from(300));

Write your own fallible conversion

Implement TryFrom<Old> for New, and name the failure type inside as type Error:

impl TryFrom<i32> for Percentage {
    type Error = String;
    fn try_from(value: i32) -> Result<Self, Self::Error> {
        // check the value, then return Ok(..) or Err(..)
    }
}

Call it with Type::try_from(value)

Percentage accepts only 0 to 100. A value in range gives Ok. A value outside gives Err with our message:

println!("{:?}", Percentage::try_from(80));
println!("{:?}", Percentage::try_from(120));

try_into: the same door, the other way

Implement TryFrom and TryInto comes free. Write the target type in the Result annotation:

let r: Result<Percentage, String> = 50i32.try_into();
println!("{:?}", r);

Example

#[derive(Debug, PartialEq)]
struct Percentage(i32);
impl TryFrom<i32> for Percentage {
    type Error = String;
    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if (0..=100).contains(&value) {
            Ok(Percentage(value))
        } else {
            Err(format!("{} is not between 0 and 100", value))
        }
    }
}
fn main() {
    println!("{:?}", Percentage::try_from(80));
    println!("{:?}", Percentage::try_from(120));
    let small: Result<u8, _> = 300i32.try_into();
    println!("300 as u8: {:?}", small);
    let ok: Result<Percentage, _> = 50i32.try_into();
    println!("50 as Percentage: {:?}", ok);
}

Key points

  • Handle the Result with match, unwrap, or ? — exactly like any other fallible call
  • TryFrom means "this might fail".
  • Picking the right pair tells your reader which promise you are making.
  • The number 300 fits in an i32, but it can never fit in a u8.
  • Error is an associated type: a type slot the trait leaves for the impl to fill in.

In this example, Percentage accepts only numbers from 0 to 100. try_from(80) passes the check, so it returns Ok(Percentage(80)). try_from(120) fails the check, so it returns Err with our message. The third line uses a conversion from the standard library. 300 does not fit in a u8. So try_into returns an Err too. The last line runs our own conversion through try_into: 50 is in range, so the result is Ok(Percentage(50)). One impl gave us both directions.

Note

From means "this always works".

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What does u8::try_from(300) return?