고급 트레이드
연관 타입
연관 타입은 자리표시자 타입을 시그니처에 사용할 수 있게 해준다. 트레이트의 구현자는 자리표시자 타입이 아닌 구체적인 타입을 지정한다.
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));
}
}