Pybites Logo Rust Platform

From and Into Traits

Easy +2 pts

🎯 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 uses From to convert error types automatically
  • Generic functions accepting impl Into<String> let callers pass &str or String
  • Standard library implements From between 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.

This is a premium exercise

Log in to unlock the full exercise and start coding.

Login to access this exercise