TL;DR:
From<T>andInto<T>are Rust's standard traits for infallible type conversion. ImplementFrom<T> for Uand you getInto<U> for Tautomatically, you only ever implementFrom. UseInto<T>in function signatures to accept multiple input types. The?operator usesFromto convert errors;String::from("hello")and"hello".into()are the same call. For fallible conversions, useTryFrom/TryIntoinstead.
What Are From and Into?
From<T> defines how to construct a type from another type. Into<T> is the mirror, implementing From<A> for B automatically gives you Into<B> for A.
// From: construct a String from a &str
let s = String::from("hello");
// Into: equivalent call; compiler infers the target type
let s: String = "hello".into();
// Both call the same code; Into is auto-derived from FromYou only ever write impl From<T> for U, never impl Into<U> for T. The standard library provides the blanket impl:
// This is in std; you get it for free
impl<T, U> Into<U> for T where U: From<T> {
fn into(self) -> U {
U::from(self)
}
}How Do You Implement From?
Implement From<OtherType> for your type to enable seamless conversions.
#[derive(Debug)]
struct Celsius(f64);
#[derive(Debug)]
struct Fahrenheit(f64);
impl From<Celsius> for Fahrenheit {
fn from(c: Celsius) -> Self {
Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
}
}
fn main() {
let boiling = Celsius(100.0);
let f = Fahrenheit::from(boiling); // explicit
println!("{:?}", f); // Fahrenheit(212.0)
let freezing = Celsius(0.0);
let f: Fahrenheit = freezing.into(); // via Into
println!("{:?}", f); // Fahrenheit(32.0)
}How Do You Use Into<T> in Function Signatures?
Accept impl Into<T> in function parameters to take any type that can be converted, callers pass values without calling .into() manually.
struct Config {
host: String,
port: u16,
}
impl Config {
fn new(host: impl Into<String>, port: u16) -> Self {
Config { host: host.into(), port }
}
}
fn main() {
// All three work; no .to_string() or String::from() needed
let c1 = Config::new("localhost", 8080); // &str
let c2 = Config::new(String::from("example.com"), 443); // String
let s = "api.server.com".to_string();
let c3 = Config::new(s, 3000); // owned String
}This is the idiomatic Rust pattern for functions that store strings, accept impl Into<String> instead of &str or String.
How Does From Power the ? Operator?
When using ? to propagate errors, Rust calls From::from(err) to convert the error type. This is how #[from] in thiserror works.
use std::num::ParseIntError;
use std::io;
#[derive(Debug)]
enum AppError {
Parse(ParseIntError),
Io(io::Error),
}
impl From<ParseIntError> for AppError {
fn from(e: ParseIntError) -> Self { AppError::Parse(e) }
}
impl From<io::Error> for AppError {
fn from(e: io::Error) -> Self { AppError::Io(e) }
}
fn parse_port(s: &str) -> Result<u16, AppError> {
let port = s.parse::<u16>()?; // ParseIntError → AppError via From
Ok(port)
}The ? operator desugars to roughly: return Err(From::from(err)).
What Is the Difference Between From/Into and TryFrom/TryInto?
From/Into are infallible, the conversion always succeeds. TryFrom/TryInto return Result<T, E> for conversions that can fail.
// Infallible; i32 always fits in i64
let x: i64 = i64::from(42i32);
// Fallible; i32 might not fit in u8
let y: Result<u8, _> = u8::try_from(300i32); // Err; 300 > 255
let z: Result<u8, _> = u8::try_from(200i32); // Ok(200)| Trait | Fallible? | Returns |
|---|---|---|
From<T> | No | U |
Into<T> | No | T |
TryFrom<T> | Yes | Result<U, E> |
TryInto<T> | Yes | Result<T, E> |
Use TryFrom/TryInto when the conversion can logically fail (overflow, invalid input). Use From/Into only when it is guaranteed to succeed.
Frequently Asked Questions
&str forces the caller to pass a string slice, they can't pass an owned String without borrowing. impl Into<String> accepts both. Inside the function, call .into() once to get the owned String. If you're storing the value, this is cleaner than s.to_string() at every call site.
Yes. You can have as many From implementations as needed, provided the source types differ.
The conversion you need doesn't exist. Either implement From<X> for Y yourself, or convert manually.
Yes, if the compiler can't infer the target type, you get "type annotations needed". Fix it with an explicit type: let x: TargetType = value.into() or use TargetType::from(value).
Sources
- std::convert::From ; Rust Standard Library
- std::convert::Into ; Rust Standard Library
- The Rust Book ; Operator Overloading
Related Glossary Terms
- Trait:
FromandIntoare standard library traits - Try Operator:
?relies onFromfor error conversion - thiserror: Uses
#[from]to generateFromimpls automatically - Generic:
impl Into<T>uses generics in function parameters
Keep Reading
- Rust Error Handling: Result and Option:
From<E>is the trait that powers the ? operator for error conversion - Rust Generics and Traits Explained: From and Into are blanket trait implementations
- Rust Ownership and Borrowing Explained: conversions often involve ownership transfer
