상세 컨텐츠

본문 제목

러스트 중급 2

Rust 예시

by 러스트코리아 2025. 9. 16. 21:36

본문

반응형

 

1.정의

트레이트는 Rust에서 타입이 반드시 구현해야 하는 메서드들의 집합을 정의하는 인터페이스입니다.
다른 언어의 인터페이스(interface)나 추상 클래스(abstract class)와 유사하지만, Rust에서는 제네릭과 함께 사용하여 제로-오버헤드 추상화를 가능하게 합니다.

✅ 핵심: 트레이트는 행위(behavior) 를 정의합니다. "이 타입은 이런 기능을 할 수 있어야 한다"는 계약(contract)입니다.

 

2. 목적

  1. 공통 인터페이스 정의 → 다양한 타입이 같은 방식으로 사용 가능
  2. 제네릭 함수에서 타입 제약 → T: Trait 형태로 사용
  3. 디스패치 방식 선택 → 정적 디스패치(static dispatch) vs 동적 디스패치(dynamic dispatch)
  4. 기본 구현 제공 → 일부 메서드는 기본 구현(default implementation) 가능
  5. 표준 라이브러리와의 통합 → Debug, Clone, PartialEq 등은 모두 트레이트

3. 예시

예시 1: 간단한 트레이트 정의 및 구현

// 트레이트 정의
trait Speak {
    fn speak(&self);
}

// 구조체 정의
struct Dog;
struct Cat;

// 각 구조체에 대해 트레이트 구현
impl Speak for Dog {
    fn speak(&self) {
        println!("멍멍!");
    }
}

impl Speak for Cat {
    fn speak(&self) {
        println!("야옹!");
    }
}

// 트레이트 바운드를 가진 제네릭 함수
fn make_speak<T: Speak>(animal: T) {
    animal.speak();
}

fn main() {
    let dog = Dog;
    let cat = Cat;

    make_speak(dog); // 멍멍!
    make_speak(cat); // 야옹!
}

 

예시 2: 기본 구현(Default Implementation)

trait Greet {
    fn greet(&self) {
        println!("Hello, world!");
    }
}

struct Person;

impl Greet for Person {} // 기본 구현 사용

fn main() {
    let p = Person;
    p.greet(); // "Hello, world!"
}

 

예시 3: 연관 타입(Associated Types)

trait Iterator {
    type Item; // 연관 타입
    fn next(&mut self) -> Option<Self::Item>;
}

struct Counter {
    count: usize,
}

impl Iterator for Counter {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        self.count += 1;
        if self.count < 5 {
            Some(self.count)
        } else {
            None
        }
    }
}

 

예시 4: 트레이트 객체 (Trait Objects) — 동적 디스패치

trait Draw {
    fn draw(&self);
}

struct Button;
struct TextField;

impl Draw for Button {
    fn draw(&self) {
        println!("Drawing a button");
    }
}

impl Draw for TextField {
    fn draw(&self) {
        println!("Drawing a text field");
    }
}

fn draw_ui(items: &Vec<Box<dyn Draw>>) {
    for item in items {
        item.draw();
    }
}

fn main() {
    let items: Vec<Box<dyn Draw>> = vec![
        Box::new(Button),
        Box::new(TextField),
    ];

    draw_ui(&items);
    // 출력:
    // Drawing a button
    // Drawing a text field
}

 

4. 메모리 및 성능: 정적 디스패치 vs 동적 디스패치

항목
정적 디스패치
동적 디스패치
컴파일 시점
모든 타입이 결정됨 →모노모피제이션
런타임에 타입 결정 →vtable 사용
성능
인라인 가능, 최적화 용이 →제로 오버헤드
간접 호출 →약간의 오버헤드
메모리
각 타입마다 별도 함수 생성 →코드 크기 증가
하나의 함수 →코드 크기 작음
유연성
컴파일 타임에 타입 고정
런타임에 다양한 타입 가능

 

5. 메모리 레이아웃 시각화

정적 디스패치 (Static Dispatch)

fn call_speak<T: Speak>(t: T) { t.speak(); }

// 컴파일러는 Dog용, Cat용 함수를 각각 생성
call_speak::<Dog>(Dog);  // → Dog::speak() 직접 호출
call_speak::<Cat>(Cat);  // → Cat::speak() 직접 호출

 

메모리 구조:

[main] → [call_speak_Dog] → [Dog::speak]
       ↘ [call_speak_Cat] → [Cat::speak]

인라인 최적화 가능, 빠름, 코드 중복

 

동적 디스패치 (Dynamic Dispatch)

fn call_speak_dyn(s: &dyn Speak) { s.speak(); }

let dog = Dog;
let cat = Cat;

call_speak_dyn(&dog); // → vtable 통해 호출
call_speak_dyn(&cat); // → vtable 통해 호출

 

메모리 구조 (vtable 기반):

&dyn Speak = [포인터: 실제 데이터, 포인터: vtable]

vtable for Dog:
  - speak: Dog::speak 주소

vtable for Cat:
  - speak: Cat::speak 주소

간접 호출, 런타임 오버헤드, 유연성 ↑

 

6. 고급 활용 : 자동 파생(Derive)

#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

// 자동으로 Debug, Clone, PartialEq 구현됨

 

7. 주의사항 

  • 객체 안전성(Object Safety): 트레이트 객체(dyn Trait)로 사용하려면 다음 조건을 만족해야 함
    • Self: Sized가 아닌 메서드만 포함
    • 연관 타입이 없거나, 제네릭이 없어야 함
    • self를 받는 메서드여야 함 (예: &self, &mut self, self)
trait NotObjectSafe {
    fn static_method(); // ❌ 객체 안전하지 않음 — self 없음
    fn with_generic<T>(); // ❌ 제네릭 있음
}

 

반응형

'Rust 예시' 카테고리의 다른 글

러스트 중급 1  (2) 2025.09.09
러스트 기초편 8  (0) 2025.09.08
러스트 기초편 7  (0) 2025.09.01
러스트 기초편 5  (4) 2025.08.30
러스트 기초편 4  (0) 2025.08.30

관련글 더보기