[Rust] 트레잇
Updated:
개요
- 다른 언어의 인터페이스와 유사
- 트레잇 혹은 타입이 우리의 크레이트 내의 것일 경우에만 해당 타입에서의 트레잇을 정의 가능
- 오버라이딩된 구현으로부터 기본 구현을 호출하는 것은 불가능
- 트레잇 바운드(trait bounds)
- 제네릭 타입 파라미터에 제약 사항을 지정
fn f<T: Summarizable>(t: T) {}
- 다수의 트레잇 바운드 가능
+
혹은where
를 이용
- 연관 타입(associated type)
- 트레잇 정의 내에서 플레이스홀더 타입을 명시
- 임의의 타입을 사용하는 트레잇을 정의 가능
- 제네릭
- 하나의 타입에 대해 제네릭 타입 파라미터의 타입을 변경해가면서 여러번 구현이 가능하므로 구현마다 타입 명시 필요
- 연관 타입은 하나의 트레잇에 대해 여러번의 구현을 할 수 없게 되므로 타입 명시를 할 필요가 없음
- 기본 제네릭 타입 파라미터
- 제네릭 타입에 대한 기본 타입 명시 가능
- 문법
<PlaceholderType=ConcreteType>
- 연산자 오버로딩이 좋은 예
- 서로 다른 트레잇에 동일한 메소드 호출
{$trait_name}::{$function_name}(&{${value}})
- 완전 정규화(fully qualified) 문법
- 동일한 이름의 메소드/연관함수를 호출해야하는 경우 모호성 방지
- 슈퍼트레잇(supertrait)
- 트레잇 내에서 다른 트레잇의 기능 요구
- 의존 중인 트레잇이 구현하는 트레잇의 슈퍼트레잇
- 뉴타입 패턴(newtype pattern)
- 외부 타입에 대해 외부 트레잇을 구현하기 위한 패턴
- 타입 안전성과 추상화를 위해서도 사용
- 튜플 구조체 내에 새로운 타입을 만드는 것
- 단점은 새로운 타입이므로 원래 값의 메소드를 가지지 못한다는 점
- 모든 메소드가 필요할 경우 Deref 트레잇을 구현하는 것이 해결책
- 일부 메소드만 필요하다면 수동 구현
예제
- 코드
-
trait TraitA { fn job() -> String { String::from("call job") } fn job1(&self) -> u32; fn job2(&self) -> String { String::from("call job2") } } trait TraitB { fn job(&self) -> u32 { 1 } } struct Test1 { i: u32, } impl TraitA for Test1 { fn job1(&self) -> u32 { self.i } } struct Test2 {} impl TraitA for Test2 { fn job1(&self) -> u32 { 1 } fn job2(&self) -> String { String::from("call job2 - Test2") } } impl TraitB for Test2 {} fn f1<T: TraitA>(t: T) -> String { t.job2() } fn f2<T: TraitA + TraitB>(t: T) -> String { t.job2() } fn f3<T>(t: T) -> String where T: TraitA + TraitB, { t.job2() } fn main() { println!("1.1 : {}", Test1::job()); println!("1.2 : {}", Test1 { i: 1 }.job1()); println!("1.3 : {}", Test1 { i: 1 }.job2()); println!("2.1 : {}", Test2 {}.job2()); println!("3.1 : {}", f1(Test1 { i: 1 })); println!("3.2 : {}", f1(Test2 {})); println!("4.1 : {}", f2(Test2 {})); println!("4.2 : {}", f3(Test2 {})); }
-
- 실행 결과
-
1.1 : call job 1.2 : 1 1.3 : call job2 2.1 : call job2 - Test2 3.1 : call job2 3.2 : call job2 - Test2 4.1 : call job2 - Test2 4.2 : call job2 - Test2
-
예제 - 기본 제네릭 타입 파라미터
- 코드
-
use std::ops::Add; struct Test1 { i: i32, } impl Add for Test1 { type Output = Test1; fn add(self, rhs: Test1) -> Test1 { Test1 { i: self.i + rhs.i } } } impl Add<Test2> for Test1 { type Output = Test1; fn add(self, rhs: Test2) -> Test1 { Test1 { i: self.i + rhs.i } } } struct Test2 { i: i32, } fn main() { let test1_1 = Test1 { i: 1 }; let test1_2 = Test1 { i: 2 }; println!("1 : {}", (test1_1 + test1_2).i); let test1 = Test1 { i: 3 }; let test2 = Test2 { i: 4 }; println!("2 : {}", (test1 + test2).i); }
-
- 실행 결과
-
1 : 3 2 : 7
-
예제 - 서로 다른 트레잇에 동일한 메소드 호출
- 코드
-
trait Trait { fn func(&self) -> String; } struct Test; impl Test { fn func(&self) -> String { String::from("aaa") } } impl Trait for Test { fn func(&self) -> String { String::from("bbb") } } fn main() { let test = Test {}; println!("1 : {}", test.func()); println!("2 : {}", Trait::func(&test)); println!("3 : {}", <Test as Trait>::func(&test)); }
-
- 실행 결과
-
1 : aaa 2 : bbb 3 : bbb
-
예제 - 완전 정규화(fully qualified) 문법
- 코드
-
trait Trait { fn func() -> String; } struct Test; impl Test { fn func() -> String { String::from("aaa") } } impl Trait for Test { fn func() -> String { String::from("bbb") } } fn main() { println!("1 : {}", Test::func()); println!("2 : {}", <Test as Trait>::func()); }
-
- 실행 결과
-
1 : aaa 2 : bbb
-
예제 - 슈퍼트레잇(supertrait)
- 코드
-
trait SuperTrait { fn func1(&self) -> String { String::from("SuperTrait::func call") } } trait Trait: SuperTrait { fn func(&self) { println!("Trait::func start"); println!("{}", self.func1()); println!("Trait::func end"); } } struct Test; impl SuperTrait for Test {} impl Trait for Test {} fn main() { let test = Test {}; test.func(); }
-
- 실행 결과
-
Trait::func start SuperTrait::func call Trait::func end
-
예제 - 뉴타입 패턴(newtype pattern)
- 코드
-
trait SuperTrait { fn func(&self) -> String { String::from("SuperTrait::func call") } } struct Test(Vec<i32>); impl SuperTrait for Test { fn func(&self) -> String { self.0[0].to_string() } } fn main() { let test = Test(vec![1, 2]); println!("{}", test.func()); }
-
- 실행 결과
-
1
-