?를 이용한 오류 전파

연산자 ?는 호출자에게 오류를 반환할 때 사용합니다. 이를 이용하면 이런 코드를

match some_expression {
    Ok(value) => value,
    Err(err) => return Err(err),
}

이렇게 짧게 쓸 수 있습니다

some_expression?

이제 우리 예제에 적용해 보겠습니다:

use std::{fs, io};
use std::io::Read;

fn read_username(path: &str) -> Result<String, io::Error> {
    let username_file_result = fs::File::open(path);
    let mut username_file = match username_file_result {
        Ok(file) => file,
        Err(err) => return Err(err),
    };

    let mut username = String::new();
    match username_file.read_to_string(&mut username) {
        Ok(_) => Ok(username),
        Err(err) => Err(err),
    }
}

fn main() {
    //fs::write("config.dat", "alice").unwrap();
    let username = read_username("config.dat");
    println!("username or error: {username:?}");
}

키 포인트:

  • username 변수는 Ok(string)이거나 Err(error)일 수 있습니다.
  • fs::write 메서드를 사용하여 파일이 없거나, 비었거나, 중복되는 경우 등을 테스트해 봅니다.
  • 함수의 리턴 타입은 네스팅 되어 호출되는 함수의 리턴 타입과 호환되어야 합니다. 예를 들어 Result<T, Err>를 리턴하는 함수는 Result<AnyT, Err>를 리턴하는 함수를 호출할 때에만?를 사용할 수 있습니다. Option<AnyT>Result<T, OtherErr> (OtherErrorFrom<Err>를 구현하지 않는 다고 가정할 때)와 같은 타입을 리턴하는 함수를 호출할 때에는 ?를 사용할 수 없습니다. 동일한 이유로, Option<T>를 리턴하는 함수는 Option<AnyT>를 리턴하는 함수를 호출할 때에만 ?를 사용할 수 있습니다.
    • OptionResult간의 변환을 위해 Option::ok_or, Result::ok, Result::err와 같은 함수들을 사용할 수 있습니다.