Updated:

3 minute read

개요

  • 다른 언어의 인터페이스와 유사
  • 트레잇 혹은 타입이 우리의 크레이트 내의 것일 경우에만 해당 타입에서의 트레잇을 정의 가능
  • 오버라이딩된 구현으로부터 기본 구현을 호출하는 것은 불가능
  • 트레잇 바운드(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