Rust 예시
러스트 중급 2
러스트코리아
2025. 9. 16. 21:36
반응형

1.정의
트레이트는 Rust에서 타입이 반드시 구현해야 하는 메서드들의 집합을 정의하는 인터페이스입니다.
다른 언어의 인터페이스(interface)나 추상 클래스(abstract class)와 유사하지만, Rust에서는 제네릭과 함께 사용하여 제로-오버헤드 추상화를 가능하게 합니다.
✅ 핵심: 트레이트는 행위(behavior) 를 정의합니다. "이 타입은 이런 기능을 할 수 있어야 한다"는 계약(contract)입니다.
2. 목적
- 공통 인터페이스 정의 → 다양한 타입이 같은 방식으로 사용 가능
- 제네릭 함수에서 타입 제약 → T: Trait 형태로 사용
- 디스패치 방식 선택 → 정적 디스패치(static dispatch) vs 동적 디스패치(dynamic dispatch)
- 기본 구현 제공 → 일부 메서드는 기본 구현(default implementation) 가능
- 표준 라이브러리와의 통합 → 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>(); // ❌ 제네릭 있음
}
반응형