16.4 Multiple error types
이전 예제는 항상 매우 편리했다; Result는 다른 Result와 그리고 Option은 다른 Option들과 상호작용 했다.
어떨 때는 Option이 Result와 상호작용 할 필요가 있거나, Result<T, Error1>이 Result<T, Error2>와 상호작용 할 필요가 있다. 이런 경우 때문에 우리의 다양한 에러 타입들을 관리하여 이들을 쉽게 상호작용 할 수 있도록 만들고자 하는 것이다.
다음의 코드에서, 두 인스턴스가 unwrap되어 다른 에러 타입을 만든다. Vec::first는 Option을 반환하고 parse::<i32>는 Result<i32, ParseIntError>를 반환한다:
fn double_first(vec: Vec<&str>) -> i32 {
let first = vec.first().unwrap(); // Generate error 1
2 * first.parse::<i32>().unwrap() // Generate error 2
}
fn main() {
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
println!("The first doubled is {}", double_first(empty));
// Error 1: the input vector is empty
println!("The first doubled is {}", double_first(strings));
// Error 2: the element doesn't parse to a number
}
연결자에 대한 우리의 지식을 활용하여, 우리는 상기 예제를 명시적으로 에러 처리를 하게끔 재작성할 수 있다. 두 다른 타입의 에러가 일어난 후에, 우리는 이들을 String고 같은 범용 타입으로 변경해야 한다.
이를 위해, 우리는 Option과 Result를 Result들로 변형하고, 그들의 에러들을 동일 타입으로 map한다:
// 에러 타입으로 `String` 사용
type Result<T> = std::result::Result<T, String>;
fn double_first(vec: Vec<&str>) -> Result<i32> {
vec.first()
// 값이 있을 시 `Option`을 `Result`로 변환.
// 그렇지 않으면, 이 `String`을 포함하는 `Err`를 제공.
.ok_or("Please use a vector with at least one element.".to_owned())
.and_then(|s| s.parse::<i32>()
// `parse`가 얻는 에러를 `String`에 맵핑한다.
.map_err(|e| e.to_string())
// `Result`은 새로운 리턴 타입이고,
// 이제 내부 숫자를 두 배로 늘릴 수 있다.
.map(|i| 2 * i))
}
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("The first doubled is {}", n),
Err(e) => println!("Error: {}", e),
}
}
fn main() {
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
print(double_first(empty));
print(double_first(strings));
} 다음 섹션에서, 명시적으로 에러를 처리하는 다른 방법을 살펴본다.