← SEARCHABLE ARCHIVE
Rust · 2026-07-31

Rust 기초부터 마스터까지 — 5화

제네릭, trait, 수명으로 재사용 가능한 API 만들기

INTERACTIVE READING MODE →

본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.

제네릭은 타입만 다른 같은 규칙을 재사용하고 trait은 타입이 제공해야 하는 동작을 정의한다. 수명 표기는 참조 사이의 관계를 컴파일러에 설명한다.

trait으로 동작 정의하기

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은 구체 타입을 몰라도 Damageable을 구현한 값을 사용할 수 있다. trait은 상속 계층보다 필요한 동작의 계약에 가깝다.

제네릭과 trait bound

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

T가 Ord를 구현한다는 조건이 있으므로 컴파일러는 값을 비교할 수 있음을 안다. 잘못된 타입 조합은 실행 전에 거부된다.

trait 구현

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);
    }
}

데이터와 공통 계약을 분리하면 외부 타입에도 허용되는 범위에서 trait을 구현할 수 있다.

수명 관계

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

'a는 반환 참조가 두 입력 참조보다 오래 살 수 없다는 관계를 나타낸다. 참조의 실제 수명을 늘리거나 런타임 자원을 만드는 기능은 아니다.

대부분의 단순한 수명은 컴파일러가 추론한다. 오류가 날 때 표기부터 늘리기보다 반환값이 어떤 입력을 빌리는지 API의 소유 관계를 먼저 확인해야 한다.