Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

?의 다른 용도

이전 예제에서 parse를 호출했을 때 우리의 즉각적인 반응은 라이브러리 에러를 박싱된 에러로 map하는 것이었습니다:

.and_then(|s| s.parse::<i32>())
    .map_err(|e| e.into())

이는 단순하고 흔한 작업이므로 생략할 수 있다면 편리할 것입니다. 아쉽게도 and_then은 충분히 유연하지 못해 생략할 수 없습니다. 하지만 대신 ?를 사용할 수 있습니다.

?는 이전에 unwrap 또는 return Err(err)로 설명되었습니다. 이는 거의 맞지만 전부는 아닙니다. 실제로는 unwrap 또는 return Err(From::from(err))를 의미합니다. From::from은 서로 다른 타입 간의 변환 유틸리티이므로, 에러가 반환 타입으로 변환 가능한 곳에서 ?를 사용하면 자동으로 변환됩니다.

여기서는 이전 예제를 ?를 사용하여 다시 작성합니다. 결과적으로, 우리의 에러 타입에 대해 From::from이 구현되면 map_err이 사라질 것입니다:

use std::error;
use std::fmt;

// 별칭이 `Box<dyn error::Error>`를 사용하도록 변경합니다.
type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

#[derive(Debug)]
struct EmptyVec;

impl fmt::Display for EmptyVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "두 배로 만들 첫 번째 항목이 유효하지 않습니다")
    }
}

impl error::Error for EmptyVec {}

// 이전과 동일한 구조이지만 모든 `Result`와 `Option`을 체인으로 연결하는 대신,
// `?`를 사용하여 즉시 내부 값을 꺼냅니다.
fn double_first(vec: Vec<&str>) -> Result<i32> {
    let first = vec.first().ok_or(EmptyVec)?;
    let parsed = first.parse::<i32>()?;
    Ok(2 * parsed)
}

fn print(result: Result<i32>) {
    match result {
        Ok(n)  => println!("두 배가 된 첫 번째 값은 {}입니다", n),
        Err(e) => println!("에러: {}", e),
    }
}

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));
}

이제 상당히 깔끔해졌습니다. 원본 panic 예제와 비교하면, 반환 타입이 Result라는 점을 제외하고는 unwrap 호출을 ?로 대체한 것과 매우 유사합니다. 그 결과, 이들은 최상위 레벨에서 구조 분해(destructure)되어야 합니다.

참고:

From::from?