트레이트는 Rust에서 타입이 반드시 구현해야 하는 메서드들의 집합을 정의하는 인터페이스입니다.
다른 언어의 인터페이스(interface)나 추상 클래스(abstract class)와 유사하지만, Rust에서는 제네릭과 함께 사용하여 제로-오버헤드 추상화를 가능하게 합니다.
✅ 핵심: 트레이트는 행위(behavior) 를 정의합니다. "이 타입은 이런 기능을 할 수 있어야 한다"는 계약(contract)입니다.
// 트레이트 정의
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); // 야옹!
}
trait Greet {
fn greet(&self) {
println!("Hello, world!");
}
}
struct Person;
impl Greet for Person {} // 기본 구현 사용
fn main() {
let p = Person;
p.greet(); // "Hello, world!"
}
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
}
}
}
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
}
항목
|
정적 디스패치
|
동적 디스패치
|
컴파일 시점
|
모든 타입이 결정됨 →모노모피제이션
|
런타임에 타입 결정 →vtable 사용
|
성능
|
인라인 가능, 최적화 용이 →제로 오버헤드
|
간접 호출 →약간의 오버헤드
|
메모리
|
각 타입마다 별도 함수 생성 →코드 크기 증가
|
하나의 함수 →코드 크기 작음
|
유연성
|
컴파일 타임에 타입 고정
|
런타임에 다양한 타입 가능
|
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]
→ 인라인 최적화 가능, 빠름, 코드 중복
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 주소
→ 간접 호출, 런타임 오버헤드, 유연성 ↑
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
// 자동으로 Debug, Clone, PartialEq 구현됨
trait NotObjectSafe {
fn static_method(); // ❌ 객체 안전하지 않음 — self 없음
fn with_generic<T>(); // ❌ 제네릭 있음
}