Result에서의 map
이전 예제의 multiply에서 패닉을 일으키는 것은 견고한 코드가 아닙니다. 일반적으로 우리는 호출자가 에러에 대응하는 올바른 방법을 결정할 수 있도록 에러를 호출자에게 반환하기를 원합니다.
먼저 우리가 어떤 종류의 에러 타입을 다루고 있는지 알아야 합니다. Err 타입을 결정하기 위해, i32에 대해 FromStr 트레이트로 구현된 parse()를 살펴봅니다. 그 결과, Err 타입은 ParseIntError로 지정됩니다.
아래 예제에서, 단순한 match 문은 전체적으로 더 번거로운 코드로 이어집니다.
use std::num::ParseIntError;
// 반환 타입을 다시 작성했으므로, `unwrap()` 없이 패턴 매칭을 사용합니다.
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
match first_number_str.parse::<i32>() {
Ok(first_number) => {
match second_number_str.parse::<i32>() {
Ok(second_number) => {
Ok(first_number * second_number)
},
Err(e) => Err(e),
}
},
Err(e) => Err(e),
}
}
fn print(result: Result<i32, ParseIntError>) {
match result {
Ok(n) => println!("n은 {}입니다", n),
Err(e) => println!("에러: {}", e),
}
}
fn main() {
// 이는 여전히 합리적인 답을 제시합니다.
let twenty = multiply("10", "2");
print(twenty);
// 다음은 이제 훨씬 더 도움이 되는 에러 메시지를 제공합니다.
let tt = multiply("t", "2");
print(tt);
}
다행히도 Option의 map, and_then 및 다른 많은 콤비네이터들이 Result에 대해서도 구현되어 있습니다. Result 문서에 전체 목록이 나와 있습니다.
use std::num::ParseIntError;
// `Option`과 마찬가지로 `map()`과 같은 콤비네이터를 사용할 수 있습니다.
// 이 함수는 위와 동일하며 다음과 같이 읽힙니다:
// 두 값 모두 문자열에서 파싱될 수 있으면 곱하고, 그렇지 않으면 에러를 넘깁니다.
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
first_number_str.parse::<i32>().and_then(|first_number| {
second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
})
}
fn print(result: Result<i32, ParseIntError>) {
match result {
Ok(n) => println!("n은 {}입니다", n),
Err(e) => println!("에러: {}", e),
}
}
fn main() {
// 이는 여전히 합리적인 답을 제시합니다.
let twenty = multiply("10", "2");
print(twenty);
// 다음은 이제 훨씬 더 도움이 되는 에러 메시지를 제공합니다.
let tt = multiply("t", "2");
print(tt);
}