0Pricing
Learn Rust Coding · 강의

트레이트 객체와 dyn

동적 디스패치

트레이트 객체와 dyn은(는) CoddyKit의 무료 Learn Rust Coding 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Learn Rust Coding 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Learn Rust Coding 강의에는 총 4개의 강의가 포함되어 있습니다.

문제: 혼합된 타입

트레이트 경계가 있는 제네릭은 각 호출에서 하나의 구체적인 타입을 사용할 때 매우 잘 작동합니다. 하지만 하나의 트레이트를 공유하는 서로 다른 타입의 컬렉션을 만들고 싶다면 어떻게 해야 할까요? 제네릭에는 여러 타입을 섞어 담을 수 없습니다.

트레이트 객체가 이 문제를 해결합니다.

트레이트 객체란 무엇인가요?

트레이트 객체는 포인터와 dyn 키워드를 통해 참조하는 값입니다. 예를 들면 &dyn Trait 또는 Box<dyn Trait>와 같습니다.

이를 사용하면 실행 중에 여러 구체적인 타입을 동일한 방식으로 다룰 수 있습니다.

dyn Trait 상자

Box<dyn Trait>는 해당 트레이트를 구현하는 어떤 타입의 힙 값을 소유합니다. 구체적인 타입을 알지 못해도 트레이트 메서드를 호출할 수 있습니다.

trait Speak { fn say(&self) -> String; }
struct Dog;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }

fn main() {
    let animal: Box<dyn Speak> = Box::new(Dog);
    println!("{}", animal.say());
}

트레이트 객체의 Vec

가장 큰 장점은 각 타입이 해당 트레이트를 구현하기만 하면 하나의 벡터에 여러 서로 다른 타입을 담을 수 있다는 점입니다.

trait Speak { fn say(&self) -> String; }
struct Dog;
struct Cat;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }
impl Speak for Cat { fn say(&self) -> String { String::from("Meow") } }

fn main() {
    let zoo: Vec<Box<dyn Speak>> = vec![Box::new(Dog), Box::new(Cat)];
    for animal in &zoo {
        println!("{}", animal.say());
    }
}

동적 디스패치

트레이트 객체에서는 숨겨진 테이블(가상 함수 테이블)을 조회하여 실행 시간에 호출할 메서드를 선택합니다. 이를 동적 디스패치라고 합니다.

그 대가로 약간의 간접 참조 비용이 들지만, 실행 시간에 유연하게 동작할 수 있습니다.

트레이트 객체를 반환하는 함수

구체적인 타입이 달라질 수 있을 때 함수는 Box<dyn Trait>을 반환할 수 있습니다. 실행 시간에 타입을 결정하는 팩토리 함수에 유용합니다.

trait Speak { fn say(&self) -> String; }
struct Dog;
struct Cat;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }
impl Speak for Cat { fn say(&self) -> String { String::from("Meow") } }

fn make(kind: &str) -> Box<dyn Speak> {
    if kind == "dog" { Box::new(Dog) } else { Box::new(Cat) }
}

fn main() {
    println!("{}", make("cat").say());
}

빌린 트레이트 객체

소유권이 필요하지 않을 때는 &dyn Trait을 사용하여 빌린 트레이트 객체를 전달할 수도 있습니다. 힙 할당은 발생하지 않습니다.

trait Speak { fn say(&self) -> String; }
struct Dog;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }

fn announce(s: &dyn Speak) {
    println!("heard: {}", s.say());
}

fn main() {
    let d = Dog;
    announce(&d);
}

객체 안전성

모든 트레이트를 트레이트 객체로 사용할 수 있는 것은 아닙니다. 트레이트는 객체 안전해야 합니다. 대략적으로 말해 메서드가 값으로 Self를 반환하거나 제네릭 타입 매개변수를 사용해서는 안 됩니다.

&self를 받고 구체적인 타입을 사용하는 메서드는 괜찮습니다.

객체에 안전하지 않은 예

Self를 반환하는 메서드가 있는 트레이트는 dyn으로 사용할 수 없습니다. 포인터 뒤에 있는 Self의 크기를 알 수 없기 때문입니다. 트레이트 객체로 사용할 트레이트는 단순하게 유지하십시오.

플러그인 방식의 예

트레이트 객체는 플러그인 시스템에서 특히 유용합니다. 핸들러 목록을 dyn 뒤에 저장하고 구체적인 타입을 신경 쓰지 않은 채 모두 실행할 수 있습니다.

trait Task { fn run(&self) -> i32; }
struct Add { a: i32, b: i32 }
struct Negate { x: i32 }
impl Task for Add { fn run(&self) -> i32 { self.a + self.b } }
impl Task for Negate { fn run(&self) -> i32 { -self.x } }

fn main() {
    let tasks: Vec<Box<dyn Task>> = vec![Box::new(Add { a: 2, b: 3 }), Box::new(Negate { x: 7 })];
    let total: i32 = tasks.iter().map(|t| t.run()).sum();
    println!("total {}", total);
}

dyn을 사용할 때

서로 다른 타입을 함께 담는 컬렉션, 실행 시간에 선택되는 타입 또는 많은 제네릭 인스턴스화로 인한 코드 부풀림을 줄여야 할 때 트레이트 객체를 사용하십시오.

그 외에는 최대한의 속도를 위해 제네릭을 우선하십시오.

빠른 확인

트레이트 객체에 대한 이해도를 확인해 보십시오.

복습

동적 디스패치를 배웠습니다.

  • 트레이트 객체는 포인터 뒤에서 dyn을 사용합니다(Box<dyn T>, &dyn T).
  • 서로 다른 타입을 함께 담는 컬렉션과 실행 시간에 선택되는 타입을 사용할 수 있습니다.
  • 메서드 호출은 가상 함수 테이블을 거칩니다(동적 디스패치).
  • 이 방식으로 사용하려면 트레이트가 객체 안전해야 합니다.

자주 묻는 질문

“트레이트 객체와 dyn” 강의는 무료인가요?

네 — “트레이트 객체와 dyn” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Learn Rust Coding 강의 전체를 잠금 해제할 수 있습니다. Learn Rust Coding 강의에는 총 4개의 강의가 포함되어 있습니다.

“트레이트 객체와 dyn”에서 뭘 배우나요?

동적 디스패치 브라우저에서 직접 실행하는 실습 코드로 Learn Rust Coding을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Learn Rust Coding을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Learn Rust Coding은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“트레이트 객체와 dyn” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Learn Rust Coding 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Learn Rust Coding 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 트레이트 정의하기
  2. 트레이트 객체와 dyn
  3. 정적 디스패치와 동적 디스패치
  4. 기본 메서드
← Learn Rust Coding(으)로 돌아가기