Rust에서 Trait Bound는 제네릭 타입이 특정 트레이트(Trait)를 구현하도록 제한하는 방식입니다. 이를 통해 컴파일러가 해당 타입이 특정 메서드를 사용할 수 있음을 보장할 수 있습니다.
Rust의 제네릭(Generic)은 기본적으로 어떤 타입이든 받을 수 있지만, 특정 기능이 필요할 경우 trait bound를 사용하여 제약을 설정할 수 있습니다.
fn print<T: ToString>(item: T) {
println!("{}", item.to_string());
}
위 함수에서 T: ToString은 T가 반드시 ToString 트레이트를 구현해야 함을 의미합니다. 따라서 print(42)처럼 숫자를 넣어도 ToString을 구현하고 있기 때문에 정상적으로 작동합니다.
여러 개의 트레이트를 적용하려면 + 연산자를 사용합니다.
fn print_and_clone<T: ToString + Clone>(item: T) {
let cloned = item.clone(); // Clone 트레이트가 필요함
println!("{}", cloned.to_string()); // ToString 트레이트가 필요함
}
where 절을 이용한 Trait Bound여러 개의 제약이 있을 경우 where을 사용하면 가독성이 좋아집니다.
fn print_and_clone<T>(item: T)
where
T: ToString + Clone,
{
let cloned = item.clone();
println!("{}", cloned.to_string());
}
impl Trait를 이용한 Trait BoundRust 1.26 이후부터는 impl Trait을 사용할 수 있습니다.
fn print(item: impl ToString) {
println!("{}", item.to_string());
}
이는 T: ToString과 동일한 역할을 하며, 가독성이 좋아지는 장점이 있습니다.
dyn Trait과 차이점impl Trait: 정적 디스패치(static dispatch) 사용, 컴파일 타임에 결정됨.dyn Trait: 동적 디스패치(dynamic dispatch) 사용, 런타임에 결정됨.fn print_dyn(item: &dyn ToString) {
println!("{}", item.to_string());
}
위와 같이 dyn Trait을 사용하면 &dyn ToString은 런타임에 타입이 결정되며, vtable을 사용하여 메서드를 호출합니다.
Default와 같은 트레이트를 활용한 제네릭 구조체 예제Trait Bound는 구조체에서도 사용할 수 있습니다.
struct Container<T: Default + Clone> {
value: T,
}
impl<T: Default + Clone> Container<T> {
fn new() -> Self {
Container { value: T::default() }
}
}
위 코드에서 T: Default + Clone을 통해 T가 Default와 Clone을 구현해야 Container<T>를 사용할 수 있습니다.
T: Trait: 기본적인 트레이트 바운드 문법T: Trait1 + Trait2: 여러 개의 트레이트 바운드 적용where T: Trait1 + Trait2: 가독성을 위한 where 절 사용impl Trait: 간결한 표현을 위해 사용dyn Trait: 런타임에서 결정되는 동적 디스패치