From/Into & TryFrom
Implement From and TryFrom for explicit, discoverable conversions between types. Prefer these over ad-hoc as casts and constructor naming soup.
Recipe
impl From<u64> for UserId {
fn from(id: u64) -> Self { UserId(id) }
}
let id: UserId = 42.into();
impl TryFrom<String> for Email {
type Error = ParseError;
fn try_from(s: String) -> Result<Self, Self::Error> { Email::parse(s) }
}When to reach for this: Boundary conversions, newtypes, parsing external input.
Working Example
struct Meters(f64);
struct Feet(f64);
impl From<Feet> for Meters {
fn from(f: Feet) -> Self { Meters(f.0 * 0.3048) }
}
impl TryFrom<&str> for UserId {
type Error = std::num::ParseIntError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(UserId(s.parse()?))
}
}What this demonstrates:
Intoauto-implemented whenFromexists- Fallible parsing uses
TryFrom+? - Conversions are symmetric and grep-friendly
- API guidelines prefer
Fromoverinto_foomethods
Deep Dive
Implement From on destination type. Chain with ? in handlers: let id = UserId::try_from(path_segment)?;. Avoid From that can fail; use TryFrom instead.
Gotchas
- Lossy From - silent truncation. Fix: only infallible conversions in
From; useTryFromfor validation. - From recursion - infinite loop. Fix: one direction only or distinct types.
- Orphan rule - cannot
impl From<Foreign> for Foreign. Fix: newtype wrapper. - Ambiguous Into - multiple targets. Fix: use turbofish
UserId::from(x). - as casts for enums - unsafe for values. Fix:
TryFrom<u8>for wire formats.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
parse() inherent | single type string parse | cross-type standard trait |
as primitive cast | numeric widening rules clear | domain validation |
map on Option | light transform | public API conversion |
FAQs
From vs custom new?
new for domain semantics; TryFrom when converting from external representation.
serde with TryFrom?
Deserialize to DTO then TryInto domain type in handler.
blanket From?
Avoid conflicting impls; follow orphan rules.
Into in generics?
fn f<T: Into<UserId>>(t: T) accepts many sources.
TryFrom error type?
Use domain error enum, not String.
ref conversions?
TryFrom<&str> common; owned TryFrom<String> avoids extra alloc when already owned.
clippy cast?
Prefer TryFrom over as for user input.
test roundtrip?
assert_eq!(x, T::from(U::from(x))) when bijection holds.
foreign crate type?
Newtype then From/TryFrom on newtype.
document conversions?
Rustdoc examples showing from, into, try_from.
Related
- The Newtype Pattern - distinct types
- Rust API Guidelines
- Parsing and Conversion
- Error-Handling Rules
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.