Step 03: Option과 Result로 에러 처리
0 studying now
core 60 min
Option과 Result로 에러 처리
Option과 Result로 안전하게 에러를 처리합니다.
Execute this step
Run from project root:
cargo runStep 3: Option과 Result로 에러 처리
학습 목표
- Option으로 "있을 수도, 없을 수도" 있는 값 표현하기
- Result로 성공과 실패를 명시적으로 처리하기
- if let으로 간결하게 패턴 매칭하기
핵심 개념
1. Option<T>
Rust는 null이 없습니다. 대신 Option<T>를 사용합니다.
1enum Option<T> { 2 Some(T), // 값이 있음 3 None, // 값이 없음 4}
예제:
1fn find_book(&self, id: u32) -> Option<&Book> { 2 self.books.get(&id) 3} 4 5// 사용 6match library.find_book(1) { 7 Some(book) => book.display(), 8 None => println!("책을 찾을 수 없습니다"), 9}
2. Result<T, E>
실패할 수 있는 연산의 결과를 표현합니다.
1enum Result<T, E> { 2 Ok(T), // 성공 3 Err(E), // 에러 4}
예제:
1fn borrow_book(&mut self, id: u32) -> Result<(), String> { 2 match self.find_book_mut(id) { 3 Some(book) => { 4 if book.is_available() { 5 book.borrow(); 6 Ok(()) 7 } else { 8 Err("이미 대여 중입니다".to_string()) 9 } 10 } 11 None => Err("책을 찾을 수 없습니다".to_string()), 12 } 13}
3. if let 패턴
하나의 패턴만 처리할 때 match보다 간결합니다.
1// match 사용 2match library.find_book(1) { 3 Some(book) => book.display(), 4 None => {}, 5} 6 7// if let 사용 (더 간결) 8if let Some(book) = library.find_book(1) { 9 book.display(); 10}
소스 코드
1use std::collections::HashMap; 2 3#[derive(Debug)] 4enum Category { 5 Fiction, 6 NonFiction, 7 Science, 8 History, 9} 10 11enum BookStatus { 12 Available, 13 Borrowed { by: String, due_date: String }, 14 Reserved { by: String }, 15 Lost, 16} 17 18struct Book { 19 id: u32, 20 title: String, 21 author: String, 22 status: BookStatus, 23 category: Category, 24} 25 26impl Book { 27 fn new(id: u32, title: String, author: String, category: Category) -> Book { 28 Book { 29 id, 30 title, 31 author, 32 status: BookStatus::Available, 33 category, 34 } 35 } 36 37 fn display(&self) { 38 print!("[{}] {} - {} ", self.id, self.title, self.author); 39 match &self.status { 40 BookStatus::Available => println!("(대여 가능)"), 41 BookStatus::Borrowed { by, due_date } => { 42 println!("(대여 중: {} - 반납일: {})", by, due_date) 43 } 44 BookStatus::Reserved { by } => println!("(예약됨: {})", by), 45 BookStatus::Lost => println!("(분실)"), 46 } 47 } 48 49 fn borrow_book(&mut self, borrower: String, due_date: String) -> bool { 50 match self.status { 51 BookStatus::Available => { 52 self.status = BookStatus::Borrowed { by: borrower, due_date }; 53 true 54 } 55 _ => false, 56 } 57 } 58 59 fn return_book(&mut self) { 60 self.status = BookStatus::Available; 61 } 62 63 fn get_category_name(&self) -> &str { 64 match self.category { 65 Category::Fiction => "소설", 66 Category::NonFiction => "비소설", 67 Category::Science => "과학", 68 Category::History => "역사", 69 } 70 } 71} 72 73struct Library { 74 books: HashMap<u32, Book>, 75 next_id: u32, 76} 77 78impl Library { 79 fn new() -> Library { 80 Library { 81 books: HashMap::new(), 82 next_id: 1, 83 } 84 } 85 86 fn add_book(&mut self, title: String, author: String, category: Category) -> u32 { 87 let id = self.next_id; 88 let book = Book::new(id, title, author, category); 89 self.books.insert(id, book); 90 self.next_id += 1; 91 println!("✓ 책 추가 완료 (ID: {})", id); 92 id 93 } 94 95 // Option 반환: 책이 있을 수도, 없을 수도 96 fn find_book(&self, id: u32) -> Option<&Book> { 97 self.books.get(&id) 98 } 99 100 fn find_book_mut(&mut self, id: u32) -> Option<&mut Book> { 101 self.books.get_mut(&id) 102 } 103 104 // Result 반환: 성공(Ok) 또는 실패(Err) 105 fn borrow_book(&mut self, id: u32, borrower: String, due_date: String) -> Result<(), String> { 106 match self.find_book_mut(id) { 107 Some(book) => { 108 if book.borrow_book(borrower.clone(), due_date) { 109 println!("✓ '{}'을(를) {}님이 대여했습니다.", book.title, borrower); 110 Ok(()) 111 } else { 112 Err(format!("'{}' 대여 불가 (이미 대여 중)", book.title)) 113 } 114 } 115 None => Err(format!("ID {}번 책을 찾을 수 없습니다", id)), 116 } 117 } 118 119 fn return_book(&mut self, id: u32) -> Result<(), String> { 120 match self.find_book_mut(id) { 121 Some(book) => { 122 book.return_book(); 123 println!("✓ '{}' 반납 완료!", book.title); 124 Ok(()) 125 } 126 None => Err(format!("ID {}번 책을 찾을 수 없습니다", id)), 127 } 128 } 129 130 fn list_all(&self) { 131 if self.books.is_empty() { 132 println!("등록된 책이 없습니다."); 133 return; 134 } 135 println!("\n=== 전체 도서 목록 ==="); 136 for book in self.books.values() { 137 book.display(); 138 } 139 } 140} 141 142fn main() { 143 println!("=== 도서 관리 시스템 v0.4 ===\n"); 144 145 let mut library = Library::new(); 146 147 // 책 추가 148 let id1 = library.add_book( 149 String::from("러스트 프로그래밍"), 150 String::from("스티브"), 151 Category::Science, 152 ); 153 let id2 = library.add_book( 154 String::from("해리포터"), 155 String::from("J.K. 롤링"), 156 Category::Fiction, 157 ); 158 let id3 = library.add_book( 159 String::from("사피엔스"), 160 String::from("유발 하라리"), 161 Category::History, 162 ); 163 164 library.list_all(); 165 166 println!(); 167 168 // Option 처리: if let 사용 169 if let Some(book) = library.find_book(id1) { 170 println!("\n책 찾기 성공:"); 171 book.display(); 172 println!("카테고리: {}", book.get_category_name()); 173 } else { 174 println!("책을 찾을 수 없습니다"); 175 } 176 177 println!(); 178 179 // Result 처리: match 사용 180 match library.borrow_book(id1, String::from("김철수"), String::from("2026-02-20")) { 181 Ok(_) => println!("대여 처리 완료"), 182 Err(e) => println!("대여 실패: {}", e), 183 } 184 185 // Result 처리: if let Err 사용 186 if let Err(e) = library.borrow_book(id1, String::from("이영희"), String::from("2026-02-25")) { 187 println!("대여 실패: {}", e); 188 } 189 190 println!(); 191 192 // 성공하는 대여 193 if let Ok(_) = library.borrow_book(id2, String::from("박민수"), String::from("2026-02-18")) { 194 println!("대여 처리 완료"); 195 } 196 197 library.list_all(); 198 199 println!(); 200 201 // 반납 202 match library.return_book(id1) { 203 Ok(_) => {}, 204 Err(e) => println!("반납 실패: {}", e), 205 } 206 207 // 존재하지 않는 책 208 if let Err(e) = library.return_book(999) { 209 println!("반납 실패: {}", e); 210 } 211 212 library.list_all(); 213}
실행 결과
1=== 도서 관리 시스템 v0.4 === 2 3✓ 책 추가 완료 (ID: 1) 4✓ 책 추가 완료 (ID: 2) 5✓ 책 추가 완료 (ID: 3) 6 7=== 전체 도서 목록 === 8[1] 러스트 프로그래밍 - 스티브 (대여 가능) 9[2] 해리포터 - J.K. 롤링 (대여 가능) 10[3] 사피엔스 - 유발 하라리 (대여 가능) 11 12책 찾기 성공: 13[1] 러스트 프로그래밍 - 스티브 (대여 가능) 14카테고리: 과학 15 16✓ '러스트 프로그래밍'을(를) 김철수님이 대여했습니다. 17대여 처리 완료 18대여 실패: '러스트 프로그래밍' 대여 불가 (이미 대여 중) 19 20✓ '해리포터'을(를) 박민수님이 대여했습니다. 21대여 처리 완료 22 23=== 전체 도서 목록 === 24[1] 러스트 프로그래밍 - 스티브 (대여 중: 김철수 - 반납일: 2026-02-20) 25[2] 해리포터 - J.K. 롤링 (대여 중: 박민수 - 반납일: 2026-02-18) 26[3] 사피엔스 - 유발 하라리 (대여 가능) 27 28✓ '러스트 프로그래밍' 반납 완료! 29반납 실패: ID 999번 책을 찾을 수 없습니다
체크리스트
- [ ] Option<T>를 반환하는 함수를 만들었습니다
- [ ] Result<T, E>를 반환하는 함수를 만들었습니다
- [ ] match로 Option과 Result를 처리했습니다
- [ ] if let으로 간결하게 패턴 매칭했습니다
- [ ] HashMap을 사용했습니다
다음 단계
Step 4에서는 소유권과 차용의 핵심 개념을 깊이 있게 배웁니다.