Rust는 안전한 시스템 프로그래밍 언어답게, 명시적이고 예측 가능한 에러 처리 방식을 제공합니다. 오늘은 Rust의 주요 에러 처리 도구인 panic, Result, Option, unwrap, expect, ? 연산자에 대해 다루겠습니다.
1. 패닉(panic!)
fn main() {
panic!("예기치 못한 오류 발생!");
}
- panic!를 사용하면 프로그램이 즉시 종료되고, panic! 안에 있는 메시지를 보여줍니다.

- 디버깅 중 주로 사용되며, 복구 불가능한 에러에 적합합니다.
let v = vec![1, 2, 3];
v[99]; // 존재하지 않는 인덱스 → 자동 panic!
Vector v의 요소가 3개이고, index의 최대값이 2인데, 인덱스를 99로 지정하면 “index가 경계를 넘어섰다”는 메시지를 보여주면서 프로그램이 멈춥니다.

2. Result<T, E> 타입
Resut<T, E>는 복구 가능한 에러 처리를 위한 열거형으로, 성공했을 때는 Ok(T), 에러가 발생했을 때는 Err(E)를 반환합니다.
enum Result<T, E> {
Ok(T),
Err(E),
}
[예제]
use std::fs::File;
fn main() {
let result = File::open("hello.txt");
match result {
Ok(file) => println!("파일 열기 성공"),
Err(e) => println!("에러 발생: {}", e),
}
}

- use std::fs::File;
: 표준 라이브러리의 fs 모듈에 정의된 File 구조체를 현재 스코프에서 사용할 수 있도록 가져오는 구문입니다. - let result = File::open(“hello.txt”);
: hello.txt 파일을 여는데, 그 결과를 result에 저장합니다. result는 Result 열거형으로 File 구조체와 Error라는 2개의 variant(변형)를 가지고 있습니다. - match result {
: match 연산자를 이용해 result의 결과값에 따라 처리합니다. - Ok(file) => println!(“파일 열기 성공”),
: 파일 열기가 성공(Ok)이면 File 구조체 형식의 file을 variant로 갖습니다. - Err(e) => println!(“에러 발생: {}”, e),
: 파일 열기에 실패하면 Err가 발생하는데, e라는 에러 종류를 갖습니다.
프로그램과 같은 폴더에 hello.txt가 없으면
“에러 발생: 지정된 파일을 찾을 수 없습니다. (os error 2)”가 화면에 출력되고,
파일이 있으면 “파일 열기 성공”이 출력됩니다.
Ok(file) => println!(“{:?} 파일 열기 성공”, file), 라고 코드를 수정하고 실행하면, hello.txt 파일이 있을 경우 file 구조체가 출력문(println!)에 전달되어
“File { handle: 0xb4, path: “\\?\D:\rust-practice\day12\hello.txt” } 파일 열기 성공”이 출력됩니다.
3. Option<T> 타입
값이 있을 수도 있고[Some(T)] 없을 수도 있음(None)을 나타냅니다. Result의 경우는 Ok(T), Err(E)인 것과 대비됩니다.
fn main() {
let some_number = Some(10);
// let some_number: Option<i32> = None;
match some_number {
Some(n) => println!("Some: {n}"),
None => println!("None"),
}
}
- let some_number = Some(10);인 상태에서 위 코드를 실행하면
Match 제어 흐름 연산자에서 Some(n)에 해당되므로 “Some: 10″이 화면에 출력되고, - 첫번째 줄 let some_number = Some(10);을 주석 처리하고, 두번째 줄의 주석을 제거하면 None과 매칭되어 “None”이 하면에 출력됩니다.
4. unwrap, expect
let f = File::open("hello.txt").unwrap(); // 실패 시 panic!
let f = File::open("hello.txt").expect("파일 열기 실패"); // 사용자 메시지 포함
- unwrap과 expect는 Some(T)이거나, Ok(T)인 경우 내부의 T를 꺼내는(unwrap) 기능을 하고, None이거나, Err(E)인 경우는 빠르게 실패(fail fast)하고 싶을 때 사용하는데, expect는 unwrap와 달리 에러 메시지를 제공합니다.
- hello.txt가 있을 경우 println!(“{:?} 파일 열기 성공”, f}를 이용해 f를 출력해보면 둘 다 아래와 같이 f를 출력합니다.
File { handle: 0xb4, path: “\\?\D:\rust-practice\day12\hello.txt” } 파일 열기 성공
File { handle: 0xb8, path: “\\?\D:\rust-practice\day12\hello.txt” } 파일 열기 성공
- 그러나, 파일이 없으면 둘다 패닉이 발생하고, 에러 메시지를 표출하는데,
let f = File::open(“hello.txt”).unwrap();은 사용자 메시지가 없고,

- let f = File::open(“hello.txt”).expect(“파일 열기 실패”);은 에러 메시지 전에 “파일 읽기 실패”라는 사용자 메시지를 표시하는 것만 다릅니다.

5. ? 연산자(에러 전파)
use std::fs::File;
use std::io::{self, Read};
fn read_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt");
let mut content = String::from("This is a test file.\n");
f.read_to_string(&mut content)?;
Ok(content)
}
fn main() {
match read_file() {
Ok(content) => println!("파일 내용:\n{}", content),
Err(e) => eprintln!("파일 읽기 오류: {}", e),
}
}
- Err이면, 다시 말해 hello.txt가 없을 경우, ?는 panic을 발생시키지 않고 즉시 리턴을 해서 main문의 Err 분기를 처리합니다.

- 그러나, ?가 없으면 다음 줄의 f.read_to_string(&mut content)?;으로 넘어가는데, read_to_string 메소드가 없다고 하면서 컴파일 에러가 발생합니다.

- Result 타입에서만 사용 가능합니다.
6. Option<T>와 Result<T, E> 비교
타입 | 의미 | 실패 시 |
---|---|---|
Option<T> | 값이 있을 수도[Some(T)], 없을 수도 있음(None) | None |
Result<T, E> | 성공[Ok(T)], 또는 실패[Err(E)] 결과 포함 | Err(E) |
둘 다 match, if let, unwrap 등을 통해 사용가능합니다.
7. 요약
도구 | 용도 | 설명 |
---|---|---|
panic! | 치명적 에러 | 프로그램 즉시 종료 |
Result<T, E> | 복구 가능한 에러 | 성공과 실패 구분 |
unwrap/expect | 빠르게 실패 | 간결하지만 안전하지 않음 |
? 연산자 | 에러 전파 | Result를 간단히 처리 |
Option<T> | 존재 여부 표현 | 값이 있을 수도 없을 수도 있음 |