---
title: "16.8 Conversions: From and Into"
description: "The From and Into traits are the blessed way to convert between types; TryFrom for fallible conversions, and how the ? operator uses From to convert error types."
url: "https://learnrust.net/chapter-16/from-and-into/"
last_updated: "2026-06-13"
---

# 16.8 Conversions: From and Into

Lesson [4.10](https://learnrust.net/chapter-4/numeric-conversions/) introduced `as` for numeric casts and promised a "blessed" conversion story for later. Later is now. The `From` and `Into` traits are how Rust converts one type into another cleanly, and they're everywhere: `String::from`, the conversions behind `.into()`, and, surprisingly, the magic that lets the `?` operator turn one error type into another. Conversions are just traits, the chapter's refrain one more time.

## The From trait

`From` is a trait for "build a value of this type from a value of that type." You've called it for ages: `String::from("hi")` is the `From<&str>` implementation for `String`. You implement `From` to teach your type how to be built from another:

```rust
struct Celsius(f64);
struct Fahrenheit(f64);

impl From<Celsius> for Fahrenheit {
    fn from(c: Celsius) -> Fahrenheit {
        Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
    }
}

fn main() {
    let body = Fahrenheit::from(Celsius(37.0));
    println!("{}", body.0);
}
```

```
98.6
```

`impl From<Celsius> for Fahrenheit` reads "a `Fahrenheit` can be made from a `Celsius`," and the `from` method does the math. Now `Fahrenheit::from(some_celsius)` works. This is a real conversion (newtypes from lesson [10.4](https://learnrust.net/chapter-10/tuple-structs-and-unit-structs/)), unlike `as`, which only reinterprets numeric bits; `From` runs whatever logic the conversion needs.

## Into comes free

Here's the elegant part: **implementing `From` automatically gives you `Into` in the other direction**, via a blanket implementation in the standard library (the same kind you saw with `Display` and `ToString` in lesson [16.6](https://learnrust.net/chapter-16/display-and-tostring/)). So once `Fahrenheit` implements `From<Celsius>`, you can also write:

```rust
fn main() {
    let c = Celsius(37.0);
    let f: Fahrenheit = c.into();   // Into, free from the From impl
    println!("{}", f.0);
}
```

```
98.6
```

`c.into()` converts `c` into whatever type the context asks for, here `Fahrenheit` (from the annotation `let f: Fahrenheit`). `From` and `Into` are two views of one conversion: `Fahrenheit::from(c)` and `c.into()` do the same thing. The rule of thumb is **implement `From`, call `into()`**: you only ever write the `From` impl, and you get the convenient `.into()` for free.

> **Key insight**
>
> `From` and `Into` are the standard, composable way to convert between types, and they're paired so you implement one and get both. This is "blessed" because the whole ecosystem agrees on it: a function that wants to accept "anything convertible into a `String`" takes `impl Into<String>`, and callers pass a `&str`, a `String`, or any type with the right `From` impl. Conversions become a shared vocabulary rather than a pile of ad-hoc `to_x` methods.

## TryFrom for conversions that can fail

Some conversions can fail: turning an `i64` into an `i32` might overflow, parsing text might not work. For those, `From`'s fallible sibling **`TryFrom`** returns a `Result` (lesson [12.2](https://learnrust.net/chapter-12/result/)) instead of a bare value:

```rust
use std::convert::TryFrom;

struct Percentage(u8);

impl TryFrom<i32> for Percentage {
    type Error = String;

    fn try_from(value: i32) -> Result<Percentage, String> {
        if (0..=100).contains(&value) {
            Ok(Percentage(value as u8))
        } else {
            Err(format!("{value} is not a valid percentage"))
        }
    }
}

fn main() {
    println!("{:?}", Percentage::try_from(50).map(|p| p.0));   // Ok(50)
    println!("{:?}", Percentage::try_from(150).map(|p| p.0));  // Err(...)
}
```

```
Ok(50)
Err("150 is not a valid percentage")
```

`TryFrom` mirrors `From` but its method, `try_from`, returns `Result<Self, Self::Error>`, so the conversion can reject bad input. Like `From`/`Into`, it has a free counterpart, `TryInto`, so `let p: Percentage = 50.try_into()?` works too. Use `From` when a conversion always succeeds, `TryFrom` when it can fail, exactly the recoverable-error judgment from lesson [12.5](https://learnrust.net/chapter-12/when-to-panic/).

## How ? uses From

Now a connection that ties chapter 12 to this one. Recall the `?` operator (lesson [12.3](https://learnrust.net/chapter-12/propagating-errors-question-mark-operator/)): it returns the error early on failure. What lesson 12 didn't say is that `?` *converts* the error as it propagates, using `From`. When you write `expr?` in a function whose error type differs from `expr`'s error type, `?` calls `From` to convert the one into the other automatically:

```rust
use std::num::ParseIntError;

#[derive(Debug)]
enum AppError {
    Parse(ParseIntError),
}

impl From<ParseIntError> for AppError {
    fn from(e: ParseIntError) -> AppError {
        AppError::Parse(e)
    }
}

fn parse_config(text: &str) -> Result<i32, AppError> {
    let n: i32 = text.parse()?;   // ParseIntError converted to AppError by ?
    Ok(n)
}
```

`text.parse()` produces a `ParseIntError`, but `parse_config` returns `AppError`. The `?` bridges the gap by calling `AppError::from(parse_error)`, which our `From` impl defines. This is why custom error enums (lesson [12.6](https://learnrust.net/chapter-12/custom-error-types/)) work so smoothly with `?`: implement `From` for each underlying error your type wraps, and `?` converts them for you, gathering many error types into your one. It's also why `Box<dyn Error>` accepts any error through `?`: there's a `From` impl that boxes any error.

## Quiz time

**Question #1**

If you implement `From<A> for B`, what conversion method do you also get for free, and in which direction?

<details class="solution">
<summary>Show solution</summary>

You get `Into<B> for A` automatically (a blanket impl in the standard library), so a value of type `A` can call `.into()` to become a `B`. `B::from(a)` and `a.into()` are the same conversion. The rule of thumb: implement `From`, call `.into()`.

</details>

**Question #2**

When should a conversion use `TryFrom` instead of `From`?

<details class="solution">
<summary>Show solution</summary>

When the conversion can fail. `From` is for conversions that always succeed and return the value directly; `TryFrom` returns a `Result`, so it can reject invalid input (an out-of-range number, unparseable text). It's the same recoverable-error judgment as lesson [12.5](https://learnrust.net/chapter-12/when-to-panic/): if failure is a real possibility, hand back a `Result`.

</details>

**Question #3**

How does the `?` operator use `From`, and why does that matter for custom error types?

<details class="solution">
<summary>Show solution</summary>

When `?` propagates an error whose type differs from the function's declared error type, it calls `From` to convert the original error into the function's error type. This means if you implement `From<UnderlyingError> for YourError` for each underlying error, `?` automatically converts them, letting one function gather several different error types into your single custom error enum (lesson [12.6](https://learnrust.net/chapter-12/custom-error-types/)) with no manual conversion code.

</details>

So far, traits have powered *generic* code, where the compiler knows each concrete type at compile time. The next lesson covers the other mode: trait objects (`dyn Trait`), which let a single collection hold values of *different* types that share a trait, choosing the right method at runtime.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
