16.6 Other uses of try!

앞의 예제에서 parse 호출 시에 우리는 즉시 라이브러리가 제공하는 에러를 우리의 사용자 정의 에러 타입으로 매핑했다:

.and_then(|s| s.parse::<i32>()
    .map_err(DoubleError::Parse)

이게 간단하고 보편적 작업이기에, 생략할 수 있다면 편리 할 것이다. 하지만 and_then이 충분히 유연하지 않기 때문에, 할 수가 없다. 그래서 우리는 try!를 대신 사용할 수 있다.

try!는 앞서 설명했 듯 unwrap이나 return Err(err) 중 하나다. 이는 거의 대부분의 경우에는 true이다. 이게 실제로 뜻하는 바는 unwrap이나 return Err(From::from(err))라는 것이다. From::from은 다른 타입 간에 변환 도구이기에, 이는 당신이 에러를 반환 타입으로 변환 가능할 때 try!하면 이는 자동으로 변환될 것이라는 뜻이다.

여기 try!로 재작성된 이전 예제가 있다. 그 결과 map_err는 사라지고 From::from이 우리의 에러 타입으로 구현되어 map_err는 사라지게 된다:

use std::num::ParseIntError;
use std::fmt;

type Result<T> = std::result::Result<T, DoubleError>;

#[derive(Debug)]
enum DoubleError {
    EmptyVec,
    Parse(ParseIntError),
}

// `ParseIntError`를 `DoubleError`로 변환하는 구현.
// `ParseIntError`가 `DoubleError`로 변환이 필요하면
// 자동으로 `try!`에 의해 호출된다.
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),
        }
    }
}

// 이전과 같은 구조이지만 `Results`와 `Options`를 모두 연결하는 대신
// 내부 값을 즉시 얻기 위해 `try!` 한다.
fn double_first(vec: Vec<&str>) -> Result<i32> {
    // `None`을 변환하는 방식을 정함을 통해 항상 `Result`를 반환한다.
    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));
}

이제는 꽤나 명료해졌다. 원본 panic과 비교했을 때, 이와 매우 유사한 것은 unwrap 호출을 try!로 변경하여 Result 을 반환하도록 하는 것이다. 결과적으로 이들은 최상위 레벨에서는 반드시 역구조화 되어야 한다

이런 종류의 에러 처리가 항상 unwrap을 대체하기를 기대해서는 안된다. 이런 타입의 에러 처리는 우리의 라인 수를 세 배로 늘렸고 정말 간결하게 될 수가 없다(심지어 적은 코드 사이즈에 큰 편견이 있더라도).

실제로 1000줄의 라이브러리를 unwrap에서 더 적절한 에러 처리로 옮기는 것은 100라인의 코드를 추가하는 것으로 가능할지도 모른다. 하지만 필요한 리팩토링은 대부분 명확히 사소한 작업이 아니다.

많은 라이브러리들이 Display만 구현하고 필요에 따라 From을 추가한다. 하지만 더 중요한 라이브러리는 에러 처리 구현에 대한 높은 기대치를 만족시켜야 한다.

See also:

From::from and try!

results matching ""

    No results matching ""