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

에러 처리: 예외(Exception)를 넘어선 명시적 설계

학습 목표: Rust가 왜 try-catch 예외 시스템 대신 Result<T, E>Option<T>를 사용하는지 이해합니다. 에러 전파를 간결하게 만드는 ? 연산자의 마법과, 모든 에러 경로를 명시적으로 다룸으로써 어떻게 더 견고한 소프트웨어를 만드는지 배웁니다.


1. 예외(Exception) vs Result 타입

C#에서는 언제 어디서 예외가 터질지 모르는 '숨겨진 제어 흐름'이 존재합니다. 반면 Rust는 에러 발생 가능성을 함수의 반환 타입에 명시합니다.

특징C# (Exceptions)Rust (Result & Option)
에러 표현throw new Exception()Err(Error) 또는 None 반환
제어 흐름스택 되감기 (런타임 비용 높음)일반적인 값 반환 (비용 없음)
강제성try-catch를 잊어도 컴파일됨반드시 처리해야 컴파일됨
가독성함수 시그니처만으로 에러 예측 불가반환 타입에 에러 종류가 명시됨

2. ? 연산자: 우아한 에러 전파

C#에서 에러를 상위로 던지기 위해 아무것도 안 하거나 throw;를 하듯, Rust에서는 ? 하나로 해결합니다.

#![allow(unused)]
fn main() {
fn read_username_from_file() -> Result<String, io::Error> {
    let mut s = String::new();
    // 파일 열기 성공하면 f에 담고, 실패하면 즉시 함수 탈출(return Err)
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}
}

3. Option<T>: Null 안전성의 완성

C#의 null은 값이 있을 수도 없을 수도 있음을 암시하지만, 체크를 강제하지는 않습니다. Rust의 Option<T>는 이를 타입 시스템으로 끌어올렸습니다.

#![allow(unused)]
fn main() {
fn find_user(id: u32) -> Option<User> {
    // 찾으면 Some(user), 못 찾으면 None 반환
}

// [사용 예시]
if let Some(user) = find_user(1) {
    println!("찾은 사용자: {}", user.name);
} else {
    println!("사용자를 찾을 수 없습니다.");
}
}

4. 패닉(Panic): 복구 불가능한 에러

모든 에러를 Result로 처리할 필요는 없습니다. 배열 인덱스 초과나 시스템 자원 고갈처럼 프로그램이 더 이상 진행될 수 없는 상황에는 panic!() 매크로를 사용하여 안전하게 프로그램을 종료합니다.


💡 실무 팁: anyhowthiserror

  • thiserror: 라이브러리를 만들 때, 명확하고 구조화된 에러 타입을 정의하기 위해 사용합니다.
  • anyhow: 애플리케이션(main 등)을 만들 때, 여러 종류의 에러를 하나로 묶어(Result<T, anyhow::Error>) 쉽고 빠르게 처리하기 위해 사용합니다.