개요

  • 대부분의 에러는 프로그램을 종료할 정도로 심각하지 않음.
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Result 사용하기

use std::fs::File;

fn main() {
    let f = File::open("hello.txt");

    let f = match f {
        Ok(file) => file,
        Err(error) => {
            panic!("There was a problem opening the file: {:?}", error)
        },
    };
}
  • File::open의 반환 타입은 Result<T, E>
    • Tstd::fs::File
    • Estd::io::Error

서로 다른 에러 매칭하기

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let f = File::open("hello.txt");

    let f = match f {
        Ok(file) => file,
        Err(ref error) if error.kind() == ErrorKind::NotFound => { // 매치 가드(match guard)
            match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => {
                    panic!("새 파일을 만드는데 실패: {:?}", e)
                }
            }
        },
        Err(error) => {
            panic!("파일을 여는데 실패: {:?}", error)
        },
    };
}
  • File::openio::Error을 반환함.
  • io::Errorkind 메소드를 제공
    • io::ErrorKind 값을 얻을 수 있다.
  • 매치 가드(match guard)
    • match 줄기 상에서 줄기의 패턴을 좀 더 정제해주는 추가 조건문.
  • ref
    • ref error가 가드 조건문으로 소유권이 이동되지 않고 참조만됨.
    • & 대신 ref가 사용되는 이유는 차후에 자세히 설명됨.
    • &는 참조자를 매치하고 그 값을 제공.
    • ref는 값을 매치하여 그 참조자를 제공.

unwrap

let f = File::open("hello.txt").unwrap();
  • Result<T, E>는 여러 헬퍼 메소드를 가지고 있다.
  • unwrap: match 구문과 비슷한 구현을 한 숏컷 메소드.
    • 만일 Result 값이 Ok variant 라면, unwrapOk 내의 값을 반환한다.
    • 만일 Result 값이 Err variant 라면, unwrappanic! 매크로를 호출한다.

expect

let f = File::open("hello.txt").expect("읽기 실패");
  • expectunwrap과 비슷하다.
    • Ok 내의 값을 반환하거나,
    • 전달한 메시지로 panic!이 호출된다.
  • unwrap은 같은 메세지로 panic!을 호출하기 때문에, expect로 특정 메시지를 사용하면 디버깅이 좀 더 수월해질것이다.

에러 전파하기

  • 실패할지 모르는 함수를 작성할 때, 함수 내에서 에러를 처리하는 대신 에러를 호출쪽으로 넘길 수 있다.
  • 에러를 어떻게 처리할지 애매한 상황에서 더 많은 정보와 로직을 가지고 있는 호출자에게 제어권 주기.
use std::io;
use std::io::Read;
use std::fs::File;

fn read_username_from_file() -> Result<String, io::Error> {

    let f = File::open("hello.txt");
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}
  • Err 경우 panic!을 호출하는 대신 함수를 일찍 끝내고 File::open 에서 발생한 에러를 호출한 함수에게 전달.
  • Err(e)의 경우 return을 명시적으로 사용하지 않아도 리턴된다.
  • Rust에서 에러를 전파하는 패턴은 너무 흔하기 때문에 더 쉽게 해주는 ? 물음표 연산자가 있다.

물음표(?) 연산자

use std::io;
use std::io::Read;
use std::fs::File;

fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}
  • ? 로 호출되고 성공하면 Ok 내의 값이 반환된다.
  • ? 로 호출되고 에러가 발생하면 호출쪽 함수는 일찍 리턴되며 전달받은 Err 값을 return
use std::io;
use std::io::Read;
use std::fs::File;

fn read_username_from_file() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}
  • 두 개의 ?중 하나라도 실패하면 전달받은 Errreturn
  • 이 함수는 최종적으로 Ok 가 리턴된다.
    • enum Result<T,E>
      • Ok(T)
      • Error<E>
  • Result를 반환하는 함수에서만 ?를 사용할 수 있다.

참고자료