advanced-traits

고급 트레이드

연관 타입

연관 타입은 자리표시자 타입을 시그니처에 사용할 수 있게 해준다. 트레이트의 구현자는 자리표시자 타입이 아닌 구체적인 타입을 지정한다.

pub trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
}

기본 제네릭 타입 매개변수

제네릭 타입에 대한 기본 구체적 타입을 지정할 수 있다.

trait Add<Rhs=Self> {
    type Output;

    fn add(self, rhs: Rhs) -> Self::Output;
}
  • 아래와 같이 확장이 가능하다.
use std::ops::Add;

struct Millimeters(u32);
struct Meters(u32);

impl Add for Millimeters {
    type Output = Millimeters;

    fn add(self, other: Millimeters) -> Millimeters {
        Millimeters(self.0 + other.0)
    }
}

impl Add<Meters> for Millimeters {
    type Output = Millimeters;

    fn add(self, other: Meters) -> Millimeters {
        Millimeters(self.0 + (other.0 * 1000))
    }
}

연산자 오버로딩

rust에선 std::ops에 나열된 연산자에 연관된 트레이트를 구현해서 연산자 오버로딩이 가능하다.

아래와 같이 std::ops::Add 트레이트를 구현하면 + 연산자를 사용할 수 있다.

use std::ops::Add;

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

모호성 방지를 위한 완전 정규화 문법: 같은 이름의 메서드 호출하기

기본적으로 러스트는 타입에 구현된 메서드를 호출해준다. 아래와 같은 방식으로 트레이트로 구현된 메서드를 호출할 수 있다.

trait Pilot {
    fn fly(&self);
}

trait Wizard {
    fn fly(&self);
}

struct Human;

impl Pilot for Human {
    fn fly(&self) {
        println!("This is your captain speaking.");
    }
}

impl Wizard for Human {
    fn fly(&self) {
        println!("Up!");
    }
}

impl Human {
    fn fly(&self) {
        println!("*waving arms furiously*");
    }
}

fn main() {
    let person = Human;
    Pilot::fly(&person);
    Wizard::fly(&person);
    person.fly();
}
  • 메서드가 아닌 연관함수에는 self가 없기 때문에 다른 방법을 사용해야한다.
  • 타입을 특정 트레이트로 취급하고 싶다고 알려주기 위해 <Type as Trait>::function(receiver_if_method, next_arg, ...);와 같은 방식을 사용한다.
trait Animal {
    fn baby_name() -> String;
}

struct Dog;

impl Dog {
    fn baby_name() -> String {
        String::from("Spot")
    }
}

impl Animal for Dog {
    fn baby_name() -> String {
        String::from("puppy")
    }
}

fn main() {
    println!("A baby dog is called a {}", Dog::baby_name());
    println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}

슈퍼트레이트를 사용하여 한 트레이트에서 다른 트레이트의 기능을 요구하기

슈퍼 트레이트: 트레이트의 정의에서 의존하고 있는 트레이트

  • trait OutlinePrint: fmt::Display와 같은 정의를 통해서 self.to_string()이 사용 가능하다.
  • OutlinePrint를 정의하는 타입에는 fmt::Display 또한 정의하고 있어야한다.
use std::fmt;

trait OutlinePrint: fmt::Display {
    fn outline_print(&self) {
        let output = self.to_string();
        let len = output.len();
        println!("{}", "*".repeat(len + 4));
        println!("*{}*", " ".repeat(len + 2));
        println!("* {} *", output);
        println!("*{}*", " ".repeat(len + 2));
        println!("{}", "*".repeat(len + 4));
    }
}