← SEARCHABLE ARCHIVE
Rust · 2026-07-31

Rust from Basics to Mastery — Part 5

Create reusable APIs with generics, traits, and lifetimes

INTERACTIVE READING MODE →

The articles written on this blog were created with the goal of teaching. Also, the information in the article may be incorrect, so please be careful.

Generics reuse the same rules just for different types, and traits define the behavior that a type must provide. Lifetime notations describe to the compiler the relationships between references.

Defining behavior with traits

trait Damageable {
    fn is_alive(&self) -> bool;
    fn take_damage(&mut self, amount: i32);
}

fn attack(target: &mut impl Damageable, damage: i32) {
    if target.is_alive() {
        target.take_damage(damage);
    }
}

Attack can use a value that implements Damageable without knowing the concrete type. A trait is closer to a contract of required behavior than an inheritance hierarchy.

Generics and trait bound

fn strongest<T>(values: &[T]) -> Option<&T>
where
    T: Ord,
{
    values.iter().max()
}

Since there is a condition that T implements Ord, the compiler knows that the values ​​can be compared. Invalid type combinations are rejected before execution.

trait implementation

struct Monster {
    health: i32,
}

impl Damageable for Monster {
    fn is_alive(&self) -> bool {
        self.health > 0
    }

    fn take_damage(&mut self, amount: i32) {
        self.health = (self.health - amount).max(0);
    }
}

By separating data and common contracts, traits can be implemented to the extent permitted by external types.

life relationship

fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() >= b.len() { a } else { b }
}

'a represents the relationship that the return reference cannot outlive the two input references. It is not a function to extend the actual lifespan of a reference or create a runtime resource.

Most simple lifetimes are inferred by the compiler. Rather than increasing the notation when an error occurs, you should first check the ownership relationship of the API to see what input the return value borrows.