Pybites Logo Rust Platform

Generic Structs

Medium +3 pts

🎯 Python's classes can hold any type without declaring it:

class Container:
    def __init__(self, item):
        self.item = item

Container(42)
Container("hello")
Container([1, 2, 3])

No type declarations needed — item can be anything. With type hints, you can be more specific:

from typing import Generic, TypeVar
T = TypeVar("T")

class Container(Generic[T]):
    def __init__(self, item: T):
        self.item = item

But Python's generics are only for type checkers. At runtime, Container[int] and Container[str] are the same class.

Rust's generic structs are different — they're real, distinct types at compile time:

struct Container<T> {
    item: T,
}

// Container<i32> and Container<String> are different types
// The compiler generates optimized code for each

Defining generic structs

The <T> after the struct name declares a type parameter. Use it in fields:

struct Container<T> {
    item: T,
}

struct Entry<K, V> {
    key: K,
    val: V,
}

Container<T> has one type parameter — both occurrences must be the same type. Entry<K, V> has two — key and val can be different types.

Implementing methods with impl

To add methods, you repeat the type parameter on impl:

impl<T> Container<T> {
    fn new(item: T) -> Self {
        Container { item }
    }

    fn unwrap(self) -> T {
        self.item
    }
}

The impl<T> says "this implementation works for any T." Self is shorthand for Container<T> — the concrete type being implemented.

self vs &self: consuming vs borrowing

Notice unwrap takes self (not &self). This means it consumes the container:

let c = Container::new(42);
let item = c.unwrap();  // c is consumed, item is 42
// c can't be used anymore — it was moved

In Python, every method gets self as a reference — the object continues to exist. In Rust, self (without &) takes ownership, destroying the original. This is the "into" naming convention: consuming the struct to extract the inner value.

Compare: - &self — borrows, original survives (like Python's self) - self — consumes, original is gone (no Python equivalent)

Swapping type parameters

A method on Entry<K, V> can return Entry<V, K> — the type parameters reversed:

impl<K, V> Entry<K, V> {
    fn flip(self) -> Entry<V, K> {
        Entry {
            key: self.val,
            val: self.key,
        }
    }
}

The return type Entry<V, K> is a different concrete type than Entry<K, V>. The compiler tracks this — after flipping, the type system knows key is now type V and val is type K.

You've already used generic structs

Vec<T>, Option<T>, Result<T, E>, HashMap<K, V> — Rust's most important types are all generic structs. Building your own follows the same pattern.

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