Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

트레이트와 제네릭: 인터페이스를 넘어선 다형성

학습 목표: Rust의 핵심 추상화 도구인 **트레이트(Traits)**를 C#의 인터페이스와 비교하며 배웁니다. 제네릭을 통한 정적 디스패치와 트레이트 객체를 통한 동적 디스패치의 차이를 이해하고, 상황에 맞는 설계를 할 수 있는 능력을 기릅니다.


1. 트레이트(Trait) vs 인터페이스(Interface)

트레이트는 C#의 인터페이스와 유사하지만, 기존 타입에 나중에 기능을 덧붙일 수 있다는 점에서 더 강력합니다.

비교 항목C# 인터페이스Rust 트레이트 (Trait)
정의 시점클래스 선언 시 함께 정의외부 타입에도 구현 가능 (고아 규칙 준수 시)
기본 구현C# 8.0부터 지원처음부터 강력하게 지원
상속인터페이스 간 상속 가능트레이트 바운드(T: A + B)로 표현
데이터 포함프로퍼티 정의 가능메서드만 정의 가능 (데이터는 구조체에)
#![allow(unused)]
fn main() {
// [트레이트 정의 및 구현 예시]
trait Summary {
    fn summarize(&self) -> String;
    
    // 기본 구현 제공
    fn read_more(&self) -> String {
        format!("(자세히 보기...)")
    }
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}: {}", self.author, self.headline)
    }
}
}

2. 정적 디스패치 vs 동적 디스패치

Rust는 성능을 위해 컴파일 타임에 모든 타입을 결정하는 방식을 선호하지만, 필요할 때는 런타임 다형성도 지원합니다.

  • 정적 디스패치 (impl Trait / 제네릭): 컴파일러가 각 타입에 맞는 코드를 따로 생성합니다 (단형성화). 성능이 가장 빠릅니다.
  • 동적 디스패치 (dyn Trait): 런타임에 vtable을 통해 메서드를 호출합니다. C#의 일반적인 인터페이스 호출 방식과 유사하며, 서로 다른 타입을 하나의 리스트에 담을 때 유용합니다.
#![allow(unused)]
fn main() {
// 정적: "Animal을 구현한 어떤 한 가지 타입 T"
fn make_it_sound<T: Animal>(item: &T) { ... }

// 동적: "Animal을 구현한 여러 타입이 섞인 무언가"
fn make_all_sound(items: &[Box<dyn Animal>]) { ... }
}

3. 주요 표준 트레이트

  • Debug / Display: 개발자용/사용자용 출력 포맷 제어
  • Clone / Copy: 데이터 복제 방식 결정
  • PartialEq / PartialOrd: 비교 및 정렬 기능
  • Default: 기본값 생성 (C#의 new() 제약 조건과 유사)
  • Iterator: 반복자 패턴 구현 (LINQ의 기반)

4. 고아 규칙 (Orphan Rule)

Rust의 엄격한 규칙 중 하나로, **"내가 만든 타입에 남의 트레이트를 구현하거나, 남의 타입에 내가 만든 트레이트를 구현할 수는 있지만, 남의 타입에 남의 트레이트를 구현할 수는 없다"**는 규칙입니다. 이는 라이브러리 간의 충돌을 방지합니다.


💡 실무 팁: derive 매크로 적극 활용하기

대부분의 공통 기능(Debug, Clone, Default 등)은 직접 구현할 필요 없이 #[derive(Debug, Clone)] 처럼 한 줄만 추가하면 컴파일러가 알아서 구현해 줍니다. 코드가 훨씬 깔끔해지고 실수를 줄일 수 있습니다.