Step 01: 트레이트 (Traits) 이해하기
foundations 60 min

트레이트 (Traits) 이해하기

트레이트로 공통 동작을 정의하고 구현합니다.

Execute this step

Run from project root:
cargo run

Step 1: 트레이트 (Traits) 이해하기

학습 목표

  • 트레이트로 공통 인터페이스 정의하기
  • 여러 타입에 트레이트 구현하기
  • 트레이트 바운드로 제네릭 제약하기

핵심 개념

1. 트레이트란?

트레이트는 다른 언어의 인터페이스와 유사합니다. 타입이 가져야 할 메서드를 정의합니다.

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

2. 트레이트 구현

1struct Book {
2    title: String,
3    author: String,
4}
5
6impl Summary for Book {
7    fn summarize(&self) -> String {
8        format!("{} by {}", self.title, self.author)
9    }
10}

3. 기본 구현

1trait Summary {
2    fn summarize(&self) -> String {
3        String::from("(더 읽기...)")
4    }
5}
6
7// 기본 구현 사용
8impl Summary for Book {}

4. 트레이트 바운드

1// 트레이트를 구현한 타입만 받기
2fn notify<T: Summary>(item: &T) {
3    println!("뉴스: {}", item.summarize());
4}
5
6// 여러 트레이트
7fn process<T: Summary + Display>(item: &T) {
8    // ...
9}
10
11// where 절 (더 읽기 쉬움)
12fn process<T>(item: &T)
13where
14    T: Summary + Display,
15{
16    // ...
17}

커스텀 트레이트 구현

src/lib.rs에 추가:

1use std::fmt;
2
3// 커스텀 트레이트: 빌릴 수 있는 것
4pub trait Borrowable {
5    fn can_borrow(&self) -> bool;
6    fn borrow_item(&mut self, borrower: String, due_date: String) -> Result<(), String>;
7    fn return_item(&mut self) -> Result<(), String>;
8}
9
10impl Borrowable for Book {
11    fn can_borrow(&self) -> bool {
12        matches!(self.status, BookStatus::Available)
13    }
14    
15    fn borrow_item(&mut self, borrower: String, due_date: String) -> Result<(), String> {
16        if self.can_borrow() {
17            self.status = BookStatus::Borrowed { by: borrower, due_date };
18            Ok(())
19        } else {
20            Err("대여할 수 없습니다".to_string())
21        }
22    }
23    
24    fn return_item(&mut self) -> Result<(), String> {
25        match self.status {
26            BookStatus::Borrowed { .. } => {
27                self.status = BookStatus::Available;
28                Ok(())
29            }
30            _ => Err("대여 중이 아닙니다".to_string()),
31        }
32    }
33}
34
35// Display 트레이트 구현
36impl fmt::Display for Book {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        let status_str = match &self.status {
39            BookStatus::Available => "대여 가능".to_string(),
40            BookStatus::Borrowed { by, due_date } => {
41                format!("대여 중: {} ({})", by, due_date)
42            }
43            BookStatus::Reserved { by } => format!("예약: {}", by),
44            BookStatus::Lost => "분실".to_string(),
45        };
46        write!(f, "[{}] {} - {} ({})", self.id, self.title, self.author, status_str)
47    }
48}
49
50impl fmt::Display for Category {
51    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52        let name = match self {
53            Category::Fiction => "소설",
54            Category::NonFiction => "비소설",
55            Category::Science => "과학",
56            Category::History => "역사",
57        };
58        write!(f, "{}", name)
59    }
60}

제네릭 함수에서 트레이트 사용

1// 트레이트 바운드를 사용한 제네릭 함수
2pub fn print_borrowable_status<T: Borrowable + fmt::Display>(item: &T) {
3    println!("{}", item);
4    if item.can_borrow() {
5        println!("  → 대여 가능!");
6    } else {
7        println!("  → 대여 불가");
8    }
9}
10
11// 사용
12let book = Book::new(1, "러스트".to_string(), "스티브".to_string(), Category::Science);
13print_borrowable_status(&book);

트레이트 객체 (동적 디스패치)

1// 여러 타입을 하나의 벡터에 저장
2pub struct LibraryItem {
3    pub item: Box<dyn Borrowable>,
4}
5
6// 나중에 DVD, Magazine 등 추가 가능
7pub struct DVD {
8    pub title: String,
9    pub available: bool,
10}
11
12impl Borrowable for DVD {
13    fn can_borrow(&self) -> bool {
14        self.available
15    }
16    // ...
17}

표준 라이브러리 트레이트

From과 Into

1// From 구현하면 Into는 자동으로 구현됨
2impl From<&str> for Category {
3    fn from(s: &str) -> Self {
4        match s.to_lowercase().as_str() {
5            "fiction" | "소설" => Category::Fiction,
6            "nonfiction" | "비소설" => Category::NonFiction,
7            "science" | "과학" => Category::Science,
8            "history" | "역사" => Category::History,
9            _ => Category::NonFiction,
10        }
11    }
12}
13
14// 사용
15let category: Category = "science".into();

실습 코드

1use book_manager::*;
2
3fn main() {
4    println!("=== 트레이트 데모 ===\n");
5    
6    let mut book = Book::new(
7        1,
8        "러스트 프로그래밍".to_string(),
9        "스티브".to_string(),
10        Category::Science,
11    );
12    
13    // Display 트레이트
14    println!("책 정보: {}", book);
15    println!("카테고리: {}", book.category);
16    
17    println!();
18    
19    // Borrowable 트레이트
20    if book.can_borrow() {
21        println!("대여 가능합니다.");
22        book.borrow_item("김철수".to_string(), "2026-02-20".to_string()).ok();
23        println!("대여 후: {}", book);
24    }
25    
26    if !book.can_borrow() {
27        println!("현재 대여 불가능합니다.");
28    }
29    
30    println!();
31    
32    book.return_item().ok();
33    println!("반납 후: {}", book);
34    
35    println!();
36    
37    // From/Into 트레이트
38    let category: Category = "fiction".into();
39    println!("변환된 카테고리: {}", category);
40    
41    // 제네릭 함수 사용
42    println!("\n=== 대여 가능 여부 확인 ===");
43    print_borrowable_status(&book);
44}

체크리스트

  • [ ] 커스텀 트레이트를 정의했습니다
  • [ ] 트레이트를 구조체에 구현했습니다
  • [ ] Display 트레이트를 구현했습니다
  • [ ] 트레이트 바운드를 사용한 제네릭 함수를 만들었습니다
  • [ ] From/Into 트레이트를 이해했습니다

다음 단계

Step 2에서는 async/await를 배우고 비동기 프로그래밍을 시작합니다.

Did you find this helpful? Give it a cheer!