From and Into Traits
🎯 Python handles type conversions with constructors and dunder methods:
n = int("42") # str → int via constructor
f = float(42) # int → float via constructor
class Celsius:
def __init__(self, value):
self.value = value
def __float__(self): # supports float(celsius_obj)
return self.value
Each conversion is ad-hoc. int() works differently from float(), and custom classes define whatever dunder methods they want. There's no single "conversion protocol" that the whole ecosystem agrees on.
Rust has one: the From trait.
The From trait
From<T> defines how to create your type from some other type T:
impl From<(u8, u8, u8)> for Color {
fn from(rgb: (u8, u8, u8)) -> Self {
Color { r: rgb.0, g: rgb.1, b: rgb.2 }
}
}
let red = Color::from((255, 0, 0));
The signature is always the same: fn from(value: T) -> Self. One input, one output, no ambiguity. Every conversion in the Rust ecosystem follows this pattern — standard library types, third-party crates, your own code.
Into: the reverse direction
Implementing From automatically gives you Into for free (via a blanket implementation in the standard library). So if Point implements From<(i32, i32)>, then (i32, i32) automatically implements Into<Point>:
// Both work after implementing From:
let c1 = Color::from((255, 0, 0)); // explicit From
let c2: Color = (0, 255, 0).into(); // implicit Into (needs type annotation)
.into() is useful when the target type is clear from context. The type annotation (: Color) tells Rust which Into implementation to use — since a tuple could .into() many different types.
Rule of thumb: implement From, use .into(). Never implement Into directly.
Tuple structs and .0 access
The exercise uses tuple structs — structs with unnamed fields accessed by position:
struct Celsius(f64); // one-field tuple struct
struct Fahrenheit(f64);
let c = Celsius(100.0);
let value = c.0; // access by index: 100.0
This is Rust's version of a newtype pattern — a thin wrapper that gives a raw value a distinct type. Python's typing.NewType is similar but only for type checkers. Rust's newtypes are real types that the compiler enforces.
From in the ecosystem
From is everywhere in Rust because it integrates with other patterns:
- The
?operator usesFromto convert error types automatically - Generic functions accepting
impl Into<String>let callers pass&strorString - Standard library implements
Frombetween all compatible types (String::from("hello"),Vec::from([1,2,3]))
When you implement From for your types, they immediately work with all these patterns.
Login to see the full task and start coding.
Topics
This is a premium exercise
Log in to unlock the full exercise and start coding.
Login to access this exercise