동작하는 코드
모듈 07에서 숫자 맞추기 게임을 만들 때 unwrap()을 사용했어요. 그때 "에러가 나면 프로그램을 멈춰라"라고만 설명했죠. 이제 그게 정확히 뭔지 알아볼 시간이에요!
fn main() {
// 파일에서 숫자를 읽는 시나리오
let input = "42";
let number: i32 = input.parse().unwrap();
println!("숫자: {number}");
let bad_input = "abc";
let result: Result<i32, _> = bad_input.parse();
println!("결과: {:?}", result);
}
실행해보세요! "42"는 잘 변환되지만, "abc"는 Err(...)가 나와요. 만약 bad_input.parse().unwrap()을 했다면 프로그램이 터졌을 거예요.
직접 수정하기
bad_input.parse().unwrap()을 직접 실행해보세요. 어떤 에러 메시지가 나오나요?input을"99999999999999999999"로 바꿔보세요.i32범위를 넘으면 어떻게 되나요?
"왜?" — 두 가지 에러
Rust는 에러를 두 종류로 나눠요.
| | 복구 불가 (panic!) | 복구 가능 (Result) | | ---- | ------------------------------------------- | -------------------------------------------- | | 비유 | 건물 화재 — 대피! | 택배 배송 실패 — 다시 보내기 | | 언제 | 프로그래밍 실수, 절대 일어나면 안 되는 상황 | 파일 없음, 네트워크 끊김 등 예상 가능한 상황 | | 처리 | 프로그램 즉시 종료 | 에러를 받아서 대응 |
panic! — 복구 불가능한 에러
panic!은 "여기서 더 진행하면 안 된다!"고 프로그램을 멈추는 거예요.
fn main() {
println!("시작!");
panic!("심각한 문제 발생!");
println!("이 줄은 실행되지 않아요");
}
인덱스 범위 초과도 panic!을 일으켜요. 모듈 06에서 본 적 있죠?
fn main() {
let v = vec![1, 2, 3];
println!("{}", v[10]); // panic!
}
Result — 택배 상태 조회
Result는 성공 또는 실패를 담는 열거형이에요. 모듈 13에서 배운 열거형이 여기서도 쓰여요!
enum Result<T, E> {
Ok(T), // 성공! 값이 들어있어요
Err(E), // 실패! 에러 정보가 들어있어요
}
택배 상태 조회에 비유하면 이해가 쉬워요.
Ok(값)— 배송 완료! 물건이 있어요Err(에러)— 배송 실패! 이유가 적혀있어요
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("0으로 나눌 수 없어요"))
} else {
Ok(a / b)
}
}
fn main() {
let result1 = divide(10.0, 3.0);
let result2 = divide(10.0, 0.0);
println!("10 / 3 = {:?}", result1);
println!("10 / 0 = {:?}", result2);
}
Result를 match로 처리하기
모듈 13에서 배운 match로 Result를 처리할 수 있어요!
fn main() {
let input = "42";
let result: Result<i32, _> = input.parse();
match result {
Ok(number) => println!("변환 성공: {number}"),
Err(e) => println!("변환 실패: {e}"),
}
}
모듈 07의 숫자 맞추기 게임에서 바로 이 패턴을 썼어요.
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("숫자를 입력해주세요!");
continue;
}
};
그때는 match를 자세히 몰랐지만, 이제는 Result의 Ok와 Err를 처리하는 것이라고 정확히 이해할 수 있어요!
unwrap() — 편하지만 위험한 친구
unwrap()은 Result에서 값을 꺼내는 가장 간단한 방법이에요. 하지만 Err일 때 panic!을 일으켜요.
fn main() {
let ok_result: Result<i32, String> = Ok(42);
let value = ok_result.unwrap(); // 42
println!("{value}");
// let err_result: Result<i32, String> =
// Err(String::from("에러!"));
// err_result.unwrap(); // panic!
}
unwrap()은 이런 뜻이에요.
Ok(값)-> 값을 꺼낸다Err(에러)->panic!으로 프로그램을 멈춘다
모듈 07에서 io::stdin().read_line(&mut guess).unwrap()을 썼는데, 만약 입력을 읽는 데 실패하면 프로그램이 그냥 죽어버리는 거였어요. 연습용 코드에서는 괜찮지만, 실제 프로그램에서는 위험할 수 있어요.
더 알아보기: expect() — unwrap()의 개선판
expect()는 unwrap()과 같지만, 에러 메시지를 직접 지정할 수 있어요.
fn main() {
let input = "abc";
let number: i32 = input
.parse()
.expect("숫자로 변환할 수 없어요");
// panic 메시지: "숫자로 변환할 수 없어요: ..."
}
어디서 에러가 났는지 알기 쉬워서, unwrap()보다 expect()가 나아요. 하지만 둘 다 panic!을 일으키는 건 같아요.
? 연산자로 에러를 우아하게 전파하는 방법은 Phase 3에서 배울 거예요!
연습 1. 아래 함수를 완성해서 문자열을 i32로 변환하세요. 변환에 실패하면 기본값 0을 반환하세요.
fn parse_or_default(s: &str) -> i32 {
// 여기를 완성하세요
// 힌트: match로 s.parse::<i32>()의 결과를 처리하세요
}
fn main() {
println!("{}", parse_or_default("42")); // 42
println!("{}", parse_or_default("abc")); // 0
println!("{}", parse_or_default("")); // 0
}
연습 2. 아래 find_item 함수를 완성하세요. 벡터에서 아이템을 찾으면 Ok(인덱스), 못 찾으면 Err(메시지)를 반환하세요.
fn find_item(items: &[&str], target: &str) -> Result<usize, String> {
// 여기를 완성하세요
// 힌트: for와 enumerate()를 사용하세요
}
fn main() {
let fruits = vec!["사과", "바나나", "포도"];
match find_item(&fruits, "바나나") {
Ok(idx) => println!("바나나는 {}번째!", idx),
Err(msg) => println!("{msg}"),
}
match find_item(&fruits, "딸기") {
Ok(idx) => println!("딸기는 {}번째!", idx),
Err(msg) => println!("{msg}"),
}
}
Q1. panic!과 Result의 가장 큰 차이는?
- A)
panic!은 느리고Result는 빠르다 - B)
panic!은 프로그램을 즉시 멈추고,Result는 에러를 값으로 반환한다 - C)
panic!은 숫자만,Result는 문자열만 다룬다 - D) 차이가 없다
Q2. unwrap()의 동작으로 올바른 것은?
- A) 항상
None을 반환한다 - B)
Ok면 값을 꺼내고,Err면panic!을 일으킨다 - C) 에러를 무시하고 빈 값을 반환한다
- D) 에러 메시지를 출력하고 계속 실행한다
Q3. 아래 코드의 출력은?
fn main() {
let result: Result<i32, &str> = Err("실패");
let value = match result {
Ok(n) => n,
Err(_) => -1,
};
println!("{value}");
}
- A) "실패"
- B) -1
- C) 0
- D) 프로그램이 panic으로 멈춘다