Menu
Coddy logo textTech

다중 경계

Coddy Rust 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 61개 중 41번째.

때로는 하나의 트레이트 바운드(trait bound)만으로는 충분하지 않을 때가 있습니다. 출력도 가능하고 동시에 요약 정보도 제공할 수 있는 제네릭 타입이 필요할 수도 있습니다. Rust에서는 + 구문을 사용하여 여러 개의 트레이트를 요구할 수 있습니다.

타입이 두 개의 트레이트를 구현해야 함을 지정하는 방법은 다음과 같습니다:

use std::fmt::Display;

trait Summary {
    fn summarize(&self) -> String;
}

fn announce<T: Display + Summary>(item: T) {
    println!("Breaking news: {}", item);
    println!("Summary: {}", item.summarize());
}

T: Display + Summary 바운드는 "T가 DisplaySummary를 모두 구현해야 함"을 의미합니다. 함수 내부에서는 {}를 사용한 출력(Display 기능)과 summarize() 호출(Summary 기능) 등 두 트레이트의 기능을 모두 사용할 수 있습니다.

필요한 만큼 많은 트레이트를 연결할 수 있습니다:

fn process<T: Display + Summary + Clone>(item: T) {
    // 출력, 요약, 그리고 복제가 가능합니다
}

이 패턴은 함수가 여러 동작에 의존할 때 필수적입니다. 어떤 타입이든 허용하고 제대로 작동하기를 바라는 대신, 필요한 기능이 정확히 무엇인지 명시적으로 선언하며, 컴파일러가 컴파일 타임에 이를 강제합니다.

challenge icon

챌린지

쉬움

아이템이 여러 기능을 갖추어야 하는 제품 검사 시스템을 구축해 봅시다! 커스텀 트레이트와 표준 트레이트를 모두 구현하는 타입만 허용하는 제네릭 함수를 만들어, + 구문이 어떻게 여러 바운드(bounds)를 결합하는지 실습해 보겠습니다.

코드는 두 개의 파일로 구성됩니다:

  • product.rs: &self를 매개변수로 받고 검사 세부 정보를 포함하는 String을 반환하는 inspect 메서드를 가진 공개(public) Inspectable 트레이트를 정의하세요. 그런 다음 공개 필드인 name (String)과 serial (u32)을 가진 공개 Gadget 구조체를 만드세요. Gadget은 Inspectable ("Inspecting: {}" 형식으로 반환)과 std::fmt::Display ("{} (SN: {})" 형식으로 포맷팅)를 모두 구현해야 합니다. 마지막으로, DisplayInspectable을 모두 구현하는 임의의 타입 T를 허용하는 공개 제네릭 함수 full_report를 만드세요. 이 함수는 두 줄을 출력해야 합니다: 첫 번째 줄은 {} 포맷터를 사용하여 아이템을 출력하고, 두 번째 줄은 inspect() 호출 결과를 출력합니다.
  • main.rs: product 모듈을 가져오고 제공된 입력을 사용하여 Gadget 인스턴스를 생성하세요. 생성한 gadget으로 full_report를 호출하여 두 트레이트 요구 사항을 모두 충족함을 보여주세요.

다중 바운드(multiple bounds)의 강력함은 full_report 함수가 두 트레이트의 기능을 모두 사용할 수 있다는 점에 있습니다. 즉, 아이템을 보기 좋게 표시하는 것과 검사 세부 정보를 가져오는 것 모두를 컴파일 타임에 보장받으며 수행할 수 있습니다.

출력은 디스플레이 형식과 검사 결과가 모두 표시되어야 합니다:

{name} (SN: {serial})
Inspecting: {name}

예를 들어, 입력값이 Smartwatch98765인 경우:

Smartwatch (SN: 98765)
Inspecting: Smartwatch

두 개의 입력을 받게 됩니다: gadget 이름과 시리얼 번호(u32로 파싱).

치트 시트

+ 구문을 사용하여 제네릭 타입에 여러 트레이트 바운드(trait bounds)를 요구할 수 있습니다:

fn function_name<T: Trait1 + Trait2>(item: T) {
    // 두 트레이트의 기능을 모두 사용할 수 있습니다
}

Display와 사용자 정의 트레이트를 사용한 예시입니다:

use std::fmt::Display;

trait Summary {
    fn summarize(&self) -> String;
}

fn announce<T: Display + Summary>(item: T) {
    println!("Breaking news: {}", item);
    println!("Summary: {}", item.summarize());
}

필요에 따라 여러 트레이트를 연결할 수 있습니다:

fn process<T: Display + Summary + Clone>(item: T) {
    // 출력, 요약, 그리고 복제(clone)가 가능합니다
}

컴파일러는 컴파일 타임에 해당 타입이 지정된 모든 트레이트를 구현했는지 확인합니다.

직접 해보기

mod product;

use product::{Gadget, full_report};

fn main() {
    // 입력 읽기
    let mut name = String::new();
    std::io::stdin().read_line(&mut name).expect("Failed to read line");
    let name = name.trim().to_string();
    
    let mut serial_input = String::new();
    std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
    let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
    
    // TODO: 이름과 시리얼 번호로 Gadget 인스턴스 생성
    
    // TODO: gadget으로 full_report 호출
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Object Oriented Programming의 모든 레슨