[Rust]

[Rust] 트레이트(trait)

극꼼 2023. 8. 18. 17:04
반응형


<트레이트(trait)>

: 타입들이 공통적으로 갖는 동작을 추상적으로 정의한 것입니다. trait bound를 이용하면 어떤 제네릭 타입 자리에 특정한 동작의 타입이 오도록 명시할 수 있습니다.

 

 

- 트레이트 구현

trait를 정의하고, 어떤 구조체(NewArticle)가 trait를 구현하도록 지정한 예시 코드입니다. trait의 메서드는 선언부만 정의할수도, 구현체까지 정의할 수도 있으며, 기본 동작을 유지할지 override할지 선택할 수 있습니다. 예시 코드에서는 트레이트에서 summarize() 함수의 선언부만 정의한 후, NewArticle에서 오버라이드했습니다.

// 신문 기사를 요약해주는 trait 구현
trait Summary {
    // 선언부만 정의
    fn summarize(&self) -> String;
}

// 기사 정보를 가진 구조체
struct NewArticle {
    pub headline: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewArticle {
    // 여기서 구현
    fn summarize(&self) -> String {
        format!("{} by {} ({})", self.headline, self.author, self.content)
    }
}

NewArticle 구조체의 인스턴스인 article을 정의한 후, summariz() 메서드를 사용할 수 있습니다.

fn main() {
    let article = NewArticle {
        headline: String::from("headline"),
        author: String::from("me"),
        content: String::from("content"),
    };
    println!("{}", article.summarize()); // headline by me (content) 출력
}

 

 

- 매개변수로서의 트레이트

위에서 작성한 예시코드에 이어서, item 매개변수가 Summary 트레이트를 사용하고 있다면 summarize 메서드를 호출하는 notify 함수입니다.

fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

fn main() {
	let article = NewArticle {
		headline: String::from("headline"),
		author: String::from("me"),
		content: String::from("content"),
    };
    notify(&article); // Breaking news! headline by me (content) 출력
}

만약 다음과 같이 Summary 트레이트를 구현하지 않은 값을 매개변수로 넣을 경우 컴파일 에러가 발생합니다 :  error[E0277]: the trait bound `Tweet: Summary` is not satisfied

struct Tweet {
    username: String,
    content: String,
    reply: bool,
    retweet: bool,
}

fn main() {
    let tweet = Tweet {
        username: String::from("name"),
        content: String::from("content2"),
        reply: true,
        retweet: false,
    };
    notify(&tweet); // 에러 발생
}

 

 

- 트레이트 바운드 문법

위에서 예시로 들었던 notify() 함수에 트레이트 바운드 문법을 적용시키면 다음과 같이 변합니다.

// 적용 전
fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

// 적용 후
fn notify2<T: Summary>(item: &T){
    println!("Breaking news! {}", item.summarize());
}

 

 

- + 구문으로 트레이트 바운드를 여럿 지정하기

트레이트 바운드는 다음과 같이 여러개 지정할 수 있습니다.

fn notify(item: &(impl Summary + Display)) { 
	// ... 
}

fn notify2<T: Summary + Display>(item: &T) { 
	// ... 
}

 

 

- where 조항으로 트레이트 바운드 정리

트레이트 바운드가 너무 많아지면 가독성을 해치므로 where을 사용해서 정리할 수 있습니다.

fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
	//...
}

// 위의 코드에 where 적용
fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: Display + Clone,
    U: Clone + Debug,
{

 

 

- 트레이트를 구현하는 타입 반환

fn returns_summarizable() -> impl Summary {
    NewArticle {
        headline: String::from("headline"),
        author: String::from("me"),
        content: String::from("content"),
    }
}

fn main() {
    println!("{}", returns_summarizable().summarize());
}

 

- 트레이트 바운드를 사용해서 조건부로 메서드 구현

x, y 필드를 가진 Pair 구조체는 new() 메서드를 사용해 새로운 인스턴스를 생성할 수 있습니다.

struct Pair<T> {
    x: T,
    y: T,
}

impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

여기서 T 타입을 정렬해서 크기비교를 할 수 있게 해주는 PartialOrd 트레이트와 출력 가능하게 해주는 Display 트레이트를 구현한 cmp_display() 메서드입니다.

impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("The largest member is x = {}", self.x);
        } else {
            println!("The largest member is y = {}", self.y);
        }
    }
}
let pair = Pair::new(4,5);
pair.cmp_display(); // The largest member is y = 5 출력

출처 : https://rust-kr.github.io/doc.rust-kr.org/ch10-02-traits.html

반응형

'[Rust]' 카테고리의 다른 글

[Rust] 라이프타임(lifetime)  (0) 2023.08.19
[Rust] 제네릭 타입  (0) 2023.08.17
[Rust] 에러 처리  (0) 2023.08.16
[Rust] match 연산자  (0) 2023.08.15
[Rust] 컬렉션(Collection)  (0) 2023.08.14