320x100
320x100

연관타입으로 트레이트 정의에서 자리표시자 타입 지정하기

연관 타입(associasted type)은 타입 자리표시자와 트레이트를 연결하여 트레이트 메서드 정의를 할 때

이러한 자리표시자 타입을 시그니처에서 사용할 수 있도록 함

트레이트의 구현자는 특정 구현을 위해서 자리 표시자 타입 대신 사용할 구체적인 타입을 지정

이렇게 하면 트레이트가 구현될 때까지 해당 타입이 무엇인지 정확히 알 필요 없이 임의의 타입을 사용하는 트레이트를 정의할 수 있음

 

연관 타입이 있는 트레이트의 한 예로 표준 라이브러리에서 제공하는 Iterator 트레이트가 있음

연관 타입의 이름은 Item이며 Iterator 트레이트를 구현하는 타입이 반복하는 값의 타입을 나타냄

pub trait Iterator {
    type Item; // 자리표시자
    
    fn next(&mut self) -> Option<Self::Item>;
}

 

 

 

 

 

기본 타입 매개변수 (default type parameter)

// Rhs: 오른쪽 (right hand side)의 줄임말 => 기본 타입 매개변수
trait Add<Rhs=Self> {
    type Output;
    
    fn add(self, rhs: Rhs) -> Self::Output;
}

 

기본 타입 매개변수는 주로 아래와 같은 상황에 쓰임

- 기존 코드를 깨는 일 없이 타입을 확장하기 위해

- 대부분의 사용자가 필요로 하지 않는 특정 상황에 대한 커스터마이징을 허용하기 위해

 

 

 

 

같은 이름의 메서드 호출하기

러스트에서는 어떤 트레이트에 다른 트레이트의 메서드와 같은 이름의 메서드가 있는 것을 막지 않으며,

한 타입에서 두 트레이트를 모두 구현하는 것도 막지 않음

또한 트레이트의 메서드와 이름이 같은 메서드를 타입에 직접 구현하는 것도 가능

 

같은 이름의 메서드를 호출할 떄는 어떤 메서드를 사용할지 러스트에 알려줘야함

이를 명확히 하기 위해서는 완전 정규화 문법을 사용해야함

<Type as Trait>::function(receiver_if_method, next_arg, ...);
trait Pilot {
    fn fly(&self);
}

trait Wizard {
    fn fly(&self);
}

struct Human;

impl pilot for Human {
    fn fly(&self) {
        println!("you can fly");
    }
}

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

impl Human {
    fn fly(&self) {
        println!("fly to the sky");
    }
}

fn main() {
    let person = Human;
    
    Pilot::fly(&person); // you can fly
    Wizard::fly(&person); // Up!!
    person.fly(); // fly to the sky
    
    // 완전 정규화 문법
    <Human as Wizard>::fly(); // Up!!
}

 

 

 

 

 

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

트레이트 정의가 의존하고 있는 트레이트를 트레이트의 슈퍼트레이트라고 함

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));
    }
}

struct Point {
    x: i32,
    y: i32,
}

impl OutlinePrint for Point {}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

 

 

 

 

 

뉴타입 패턴을 사용하여 외부 타입에 외부 트레이트 구현하기

트레이트나 타입이 우리 크레이트의 것인 경우에만 타입에 트레이트를 구현할 수 있다는 고아 규칙이 있는데,

튜플 구조체로 새로운 타입을 생성하는 뉴타입 패턴(newtype pattern)을 사용하면 이 제한을 우회할 수 있음

 

튜플 구조체는 하나의 필드를 가지며 트레이트를 구현하고자 하는 타입을 얇게 감싸는 래퍼가 됨

그러면 래퍼 타입은 우리 크레이트 내에 있게 되어 래퍼에 대한 트레이트를 구현할 수 있음

 

뉴 타입은 하스켈 프로그래밍 언어에서 유래한 용어이고,

이 패턴을 사용해도 런타임 성능에 대한 불이익은 없으며 래퍼 타입은 컴파일 시 제거됨

use std::fmt;

struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

fn main() {
    let w = Wrapper(vec![String::from("hello"), String::from("world")]);
    println!("w = {}", w);
}

 

300x250
728x90

'Programming > Rust' 카테고리의 다른 글

러스트 고급 함수와 클로저  (0) 2025.10.12
러스트 고급 타입  (0) 2025.10.12
안전하지 않은 러스트  (0) 2025.10.12
러스트 패턴과 매칭  (0) 2025.10.11
러스트의 객체 지향 프로그래밍 기능  (0) 2025.10.11