BradCypert.com
From and Into in Rust
October 23, 2022

From and Into in Rust

Posted on October 23, 2022  (Last modified on March 20, 2024 )
4 minutes  • 668 words
This project uses these versions of languages, frameworks, and libraries.
  • rust rust : 2018
This tutorial may work with newer versions and possibly older versions, but has only been tested on the versions mentioned above.

Rust's trait system is like a breath of fresh air when compared to some of more recent language forays. One of my favorite things about Rust and it's trait system is how the From and Into traits work.

The From trait is a trait that you can implement on your own types, but its also implemented on a few existing types , too. From allows you to specify code to generate something of type X from something of type Y. For example, the standard library implements impl From<&'_ str> for String which allows you to generate a String from a &str.

You're also able to implement From for your own types. For example, you may implement From<String> for Email. This would allow you to generate an email from a String. Let's go ahead and implement that now (pardon the contrived example).

struct Email {
    value: String,
}

impl From<String> for Email {
    fn from(item: String) -> Self {
        Email { value: item }
    }
}

But how is From used?

Using from actually has a few different options. Let's follow along from our above example that uses the email struct that we've defined.

fn main() {
    let my_email = "[email protected]";
    let example1 = Email::from(my_email);
    let example2: Email = my_email.into();
}

You'll notice two different examples here. We can explicitly use the from method that we've implemented on our Email, however, we can also use .into() -- but there's a catch. into() cant be inferred. Imagine we have multiple implementers of From<String> -- would it create an Email, an Address, or something different? We have to explicitly annotate the type of the variable that the into() is being stored in. This helps the rust compiler figure out which From implementation is being called. Technically, we may not have to annotate the type if the compiler can figure it out on its own, but at the time of writing, the compiler seems to need help here.

Into?

A natural question at this point is where is that "into" method defined? The Rust docs do a great job explaining this, so I'll share what they have here :

The From and Into traits are inherently linked, and this is actually part of its implementation. It means if we write something like this: impl From for U, then we can use let u: U = U::from(T) or let u:U = T.into().

Because the Into trait is linked and is a reciprocal trait, it will automatically be implemented for types that implement From.

You're free to use into() and from() both! It's ultimately up to you to decide which one feels the most expressive to your code and that's the one I'd recommend using!

Two traits? Which to implement?

If both of these traits exist and are reciprocals, a natural question is which one do I implement? The answer is From. Implementing From will generate the Into implementation for you, but the inverse is not (to my knowledge) true.

Trys

From and Into have TryFrom and TryInto (respectively) counterparts. You're able to implement TryFrom just like you would From and TryInto will be generated for you. Our email example is a great one here -- there's a good chance that String isn't an email! Let's go ahead and implement TryFrom so we can return a Result with our Email type.

use std::convert::TryFrom;
use std::convert::TryInto;

struct Email {
    pub value: String,
}

impl TryFrom<String> for Email {
    type Error = ();

    fn try_from(item: String) -> Result<Self, Self::Error> {
        if item.ends_with(".com") {
            Ok(Email {
                value: item
            })
        } else {
            Err(())
        }
    }
}

fn main() {
    let email: Result<Email, ()> = String::from("[email protected]").try_into();
    println!("{}", email.unwrap().value);
}

As you can see, TryFrom is nice when theres a possibility that the conversion fails. The alternative with using From is to either allow non-email Strings to be used in our Email struct or to panic (and you probably dont want to do this).

Hopefully this helps explain the From and Into traits, as well as TryFrom and TryInto.

Cartoon headshot of Brad Cypert
Follow me

Connect with me to follow along on my journey in my career, open source, and mentorship. Occasionally, I'll share good advice and content (quality not guaranteed).