16.7 Box
ing errors
Display
와 From
을 우리 에러 타입을 위해 구현함으로, 우리는 std
라이브러리 에러 처리 도구들을 거의 대부분 사용할 수 있었다. 하지만, 우리가 놓치고 있는 것이 있었으니: 쉽게 우리의 에러 타입을 Box
하는 능력.
std
라이브러리들은 From
을 통해 Error
trait을 구현하는 모든 타입은 trait 객체 Box<Error>
로 자동 변환된다. 라이브러리 유저에게 이는 다음과 같은 편의성을 제공한다:
fn foo(...) -> Result<T, Box<Error>> { ... }
사용자가 사용할 수 있는 다양한 외부 라이러리들은 각기 그들 자신의 고유한 에러 타입을 제공한다. 적절한 Result<T, E>
타입을 정의하기 위하여, 사용자는 몇 가지 선택지가 있다:
- 라이브러리의 에러 타입을 포장하는 새 포장된 에러 정의
- 에러 타입을
String
이나 다른 중간 형태를 선택해 타입 변환 - 에러 타입들을
Box
하여Box<Error>
로 타입 소멸
"박싱(Boxing)"은 보편적으로 선택되는 에러 타입이다. 단점은 기본 에러 타입을 오직 런타임에만 알 수 있고 정적으로 결정되지 않는다. 위에서 언급했듯, Error
trait을 구현하는 것이 필요하다:
trait Error: Debug + Display {
fn description(&self) -> &str;
fn cause(&self) -> Option<&Error>;
}
이 구현을 통해 가장 최근의 예제를 살펴보자. DoubleError
였을 때와 마찬가지로 Box<Error>
일 때도 유효하다:
use std::error;
use std::fmt;
use std::num::ParseIntError;
// 별칭을 `Box`로 변경.
type Result<T> = std::result::Result<T, Box<error::Error>>;
#[derive(Debug)]
enum DoubleError {
EmptyVec,
Parse(ParseIntError),
}
impl From<ParseIntError> for DoubleError {
fn from(err: ParseIntError) -> DoubleError {
DoubleError::Parse(err)
}
}
impl fmt::Display for DoubleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DoubleError::EmptyVec =>
write!(f, "please use a vector with at least one element"),
DoubleError::Parse(ref e) => e.fmt(f),
}
}
}
impl error::Error for DoubleError {
fn description(&self) -> &str {
match *self {
// 에러에 대한 짧은 설명. `Display`와 같을 필요 없다.
DoubleError::EmptyVec => "empty vectors not allowed",
// 이것은 이미 `Error`를 구현하므로, 그의 구현에 위임한다.
DoubleError::Parse(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
// 아래 줄이 발생하지 않으니 `None`을 반환한다.
DoubleError::EmptyVec => None,
// 아래 구현된 에러 타입을 유발되면 암시적으로 `&error:Error`로 변환된다.
// 이는 아래 타입이 이미 `Error` trait을 구현하기 때문에 동작한다.
DoubleError::Parse(ref e) => Some(e),
}
}
}
fn double_first(vec: Vec<&str>) -> Result<i32> {
let first = try!(vec.first().ok_or(DoubleError::EmptyVec));
let parsed = try!(first.parse::<i32>());
Ok(2 * parsed)
}
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("The first doubled is {}", n),
Err(e) => println!("Error: {}", e),
}
}
fn main() {
let numbers = vec!["93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
print(double_first(numbers));
print(double_first(empty));
print(double_first(strings));
}