트레이트를 활용한 타입 반환
Coddy Rust 여정의 Object Oriented Programming 섹션에 포함된 레슨. 61개 중 43번째.
trait bounds를 사용해 function이 어떤 타입을 accept할 수 있는지 제한해 보았습니다. 그렇다면 function이 무엇을 반환하는지는 어떨까요? impl Trait 구문을 사용하면 concrete 타입의 이름을 지정하지 않고도 function이 "이 trait을 implements하는 어떤 타입"을 반환한다고 지정할 수 있습니다.
다음은 구문이 실제로 사용되는 모습입니다:
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
headline: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
self.headline.clone()
}
}
fn create_summary() -> impl Summary {
Article { headline: String::from("Breaking News!") }
}
반환 타입 impl Summary은 호출자에게 "호출자는 Summary를 구현하는 무언가를 받게 됩니다."라고 알려 줍니다. 호출자는 트레이트의 어떤 method든 사용할 수 있지만, 실제 타입이 Article이라는 사실은 알 필요가 없습니다.
이는 concrete 타입이 복잡하거나 구현 세부 정보를 숨기고 싶을 때 특히 유용합니다. function은 특정 타입이 아니라 기능(trait)을 약속합니다.
하지만 중요한 제한이 있습니다. function은 정확히 하나의 concrete 타입만 return해야 합니다. 동일한 트레이트를 구현하는 서로 다른 타입을 조건부로 return할 수는 없습니다. 이를 위해서는 트레이트 객체가 필요하며, 이에 대해서는 나중에 배우게 됩니다.
// 이것은 작동합니다 - 항상 Article을 반환합니다
fn make_item() -> impl Summary {
Article { headline: String::from("Hello") }
}
// 이것은 컴파일되지 않습니다 - 두 가지 다른 타입
// fn make_item(flag: bool) -> impl Summary {
// if flag { Article { ... } } else { Tweet { ... } }
// }
챌린지
쉬움impl Trait 반환 타입을 사용하여 구현 세부 정보를 숨기는 greeting card factory를 만들어 보세요! 호출자에게 구체적인 타입을 공개하지 않고 "something that can greet"를 반환하는 function을 만들게 됩니다.
코드를 두 파일에 나누어 구성합니다:
greetings.rs:Greettrait을 정의하고,&self를 받아String을 반환하는greetmethod를 포함하도록 합니다. 그런 다음Cardstruct를 만들고(반드시 public일 필요는 없습니다!)messagefield(String)를 추가합니다.Card에Greettrait을Implement하고 message를 Return합니다. 마지막으로Stringparameter를 받고impl Greet를 반환하는create_greeting이라는 publicfunction을 만듭니다. 이function은 주어진 message를 담은Card를 만들고 반환해야 합니다.main.rs: greetings module을 가져오고 제공된 input을 사용하여create_greeting을 호출합니다. 여기서 중요한 점은 main 파일이Card타입에 대해 전혀 알지 못한다는 것입니다.Greet을 implements하는 무언가를 받았다는 것만 압니다. 반환된 값에서greetmethod를 호출하고 result를 print합니다.
핵심 통찰은 Card가 module 내부에서는 private으로 유지되지만, 호출자는 여전히 trait interface를 통해 이를 사용할 수 있다는 것입니다. function은 특정 타입이 아니라 capability( greeting)를 약속합니다.
출력에는 greeting이 표시되어야 합니다:
{message}예를 들어 input이 Happy Birthday!인 경우:
Happy Birthday!그리고 input이 Congratulations on your promotion!인 경우:
Congratulations on your promotion!하나의 input, 즉 greeting message를 받습니다.
직접 해보기
mod greetings;
use greetings::Greet;
fn main() {
// 입력에서 인사 메시지를 읽습니다
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let message = input.trim().to_string();
// TODO: 메시지와 함께 create_greeting을 호출하세요
// Note: 구체적인 타입은 모릅니다 - Greet를 구현한다는 것만 압니다!
// TODO: greet 메서드를 호출하고 결과를 출력하세요
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
Object Oriented Programming의 모든 레슨
직접 연습해 보세요: 온라인 Rust 컴파일러