에러 감싸기
에러를 박싱하는 것의 대안은 에러를 여러분만의 에러 타입으로 감싸는 것입니다.
use std::error;
use std::error::Error;
use std::num::ParseIntError;
use std::fmt;
type Result<T> = std::result::Result<T, DoubleError>;
#[derive(Debug)]
enum DoubleError {
EmptyVec,
// 파싱 에러에 대해서는 해당 에러 구현에 위임할 것입니다.
// 추가 정보를 제공하려면 타입에 더 많은 데이터를 추가해야 합니다.
Parse(ParseIntError),
}
impl fmt::Display for DoubleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DoubleError::EmptyVec =>
write!(f, "최소한 하나의 요소를 가진 벡터를 사용하세요"),
// 감싸진 에러는 추가 정보를 포함하며 `source()` 메서드를 통해
// 사용할 수 있습니다.
DoubleError::Parse(..) =>
write!(f, "제공된 문자열을 정수로 파싱할 수 없습니다"),
}
}
}
impl error::Error for DoubleError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
DoubleError::EmptyVec => None,
// 원인은 기저 구현 에러 타입입니다. 이는 트레이트 객체 `&error::Error`로
// 암시적으로 캐스팅됩니다. 기저 타입이 이미 `Error` 트레이트를
// 구현하고 있기 때문에 작동합니다.
DoubleError::Parse(ref e) => Some(e),
}
}
}
// `ParseIntError`에서 `DoubleError`로의 변환을 구현합니다.
// `ParseIntError`를 `DoubleError`로 변환해야 할 때 `?`에 의해
// 자동으로 호출됩니다.
impl From<ParseIntError> for DoubleError {
fn from(err: ParseIntError) -> DoubleError {
DoubleError::Parse(err)
}
}
fn double_first(vec: Vec<&str>) -> Result<i32> {
let first = vec.first().ok_or(DoubleError::EmptyVec)?;
// 여기서 우리는 `DoubleError`를 생성하기 위해 (위에서 정의한)
// `From`의 `ParseIntError` 구현을 암시적으로 사용합니다.
let parsed = first.parse::<i32>()?;
Ok(2 * parsed)
}
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("두 배가 된 첫 번째 값은 {}입니다", n),
Err(e) => {
println!("에러: {}", e);
if let Some(source) = e.source() {
println!(" 원인: {}", source);
}
},
}
}
fn main() {
let numbers = vec!["42", "93", "18"];
let empty = vec![];
let strings = vec!["두부", "93", "18"];
print(double_first(numbers));
print(double_first(empty));
print(double_first(strings));
}
이는 에러 처리를 위해 상용구(boilerplate) 코드를 조금 더 추가하며, 모든 애플리케이션에서 필요하지는 않을 수 있습니다. 상용구 코드를 대신 처리해 주는 몇몇 라이브러리들이 있습니다.