
안녕하세요.
오늘은 Rust의 에러 처리에 대해 알아보도록 하겠습니다.
📌 Rust의 에러 처리
Rust는 Exception 대신, 오류를 2가지로 나눠서 에러를 처리합니다.
- 복구 가능한 오류 (Recoverable Errors) → Result<T, E> 사용
- 복구 불가능한 오류 (Unrecoverable Errors) → panic! 사용
📌 복구 가능한 오류 (Recoverable Errors)
✅ 정의
- 프로그램이 정상적으로 실행을 이어나갈 수 있는 오류 입니다
- 적절한 오류 처리 후 다시 실행을 이어나갈 수 있습니다
✅ 해결 방법
- Result<T, E>를 사용합니다
- 호출자가 에러를 직접 처리할 수 있도록 반환합니다
예제) 파일 읽을 때 오류 발생
파일이 존재하지 않으면, 새 파일을 생성하는 방식으로 복구 가능한 예제입니다
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let file = File::open("hello.txt");
let file = match file {
Ok(file) => file, // ✅ 성공하면 파일을 반환
Err(error) => match error.kind() {
ErrorKind::NotFound => {
println!("파일이 없습니다. 새 파일을 생성합니다.");
File::create("hello.txt").unwrap() // ❗ 복구: 새 파일 생성
}
other_error => panic!("파일을 열 수 없습니다: {:?}", other_error),
},
};
}
📌 복구 불가능한 에러 (Unrecoverable Errors)
✅ 정의
- 프로그램이 정상적으로 실행될 수 없는 오류를 의미합니다
- 잘못된 상태에서 계속 실행되는 것을 방지하기 위해 있는 에러입니다
✅ 해결 방법
- panic!을 사용하여 프로그램을 즉시 종료합니다
예제) 배열 인덱스 초과
fn main() {
let numbers = vec![1, 2, 3];
println!("{}", numbers[10]); // ❌ 존재하지 않는 인덱스 접근 → panic!
}

📌 복구 불가능한 에러 처리 방법
위의 예제에서는 panic!을 일으켜 프로그램이 종료되지만,
Java 언어에서는 IndexOutOfBound Exception을 반환해, try-catch로 감싸서 프로그램이 종료되는 것을 방지합니다
이처럼 Rust 에서도 프로그램을 강제 중단시키고 싶지 않은 경우에, 어떻게 처리를 할 수 있을까요?
대표적으로 2가지가 있습니다.
✅ solve1) get() 메서드 사용
get() 메서드를 사용해서 index에 접근하면, Option<T>를 반환 받습니다
- index가 배열 범위 내라면 → Some<T> 반환
- index가 배열 범위 외라면 → None 반환
fn main() {
let numbers = vec![1, 2, 3];
match numbers.get(10) {
Some(value) => println!("값: {}", value),
None => println!("잘못된 인덱스 접근!"), // ❌ panic 발생 X
}
}
✅ solve2) catch_unwind 사용
위의 방식은 패닉을 사전에 차단하는 방식이라면,
catch_unwind 방식은 패닉이 일어난 이후 처리하는 사후 처리 방식 입니다.
use std::panic;
fn main() {
let result = panic::catch_unwind(|| {
let numbers = vec![1, 2, 3];
println!("{}", numbers[10]); // ❌ panic 발생
});
if result.is_err() {
println!("패닉이 발생했지만 복구했습니다.");
}
}
C:/Users/kdr06/.cargo/bin/cargo.exe run --color=always --package practice1 --bin practice1 --profile dev
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `target\debug\practice1.exe`
thread 'main' panicked at src\main.rs:6:31:
index out of bounds: the len is 3 but the index is 10
stack backtrace:
0: std::panicking::begin_panic_handler
at /rustc/e71f9a9a98b0faf423844bf0ba7438f29dc27d58\library/std\src\panicking.rs:665
1: core::panicking::panic_fmt
at /rustc/e71f9a9a98b0faf423844bf0ba7438f29dc27d58\library/core\src\panicking.rs:76
2: core::panicking::panic_bounds_check
at /rustc/e71f9a9a98b0faf423844bf0ba7438f29dc27d58\library/core\src\panicking.rs:281
3: core::slice::index::impl$2::index<i32>
at C:\Users\kdr06\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\slice\index.rs:274
4: alloc::vec::impl$13::index<i32,usize,alloc::alloc::Global>
at C:\Users\kdr06\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\alloc\src\vec\mod.rs:3346
5: practice1::main::closure$0
at .\src\main.rs:6
6: std::panicking::try::do_call<practice1::main::closure_env$0,tuple$<> >
at C:\Users\kdr06\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:557
7: std::panic::catch_unwind<practice1::main::closure_env$0,tuple$<> >
8: std::panicking::try
at C:\Users\kdr06\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:520
9: std::panic::catch_unwind<practice1::main::closure_env$0,tuple$<> >
at C:\Users\kdr06\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:358
10: practice1::main
at .\src\main.rs:4
11: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
at C:\Users\kdr06\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:250
12: core::hint::black_box
at C:\Users\kdr06\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\hint.rs:389
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
패닉이 발생했지만 복구했습니다.
Process finished with exit code 0
❌ 이 방식은 권장되는 방식은 아님!
panic!이 발생한 뒤에는 프로그램이 예측할 수 없는 상태가 되어 버립니다.
억지로 복구하는 방법 대신, 사전에 방지하는 방식을 사용하는 것이 좋습니다.
📌 ?연산자
에러 처리를 간결하게 만들기 위해 ?연산자를 사용할 수 있습니다
- Err를 만나면 즉시 호출자에게 전파합니다
예제) 0으로 나누기
0으로 나누는 코드는 Unrecoverable Error로, panic!을 일으킵니다
fn divide(a: i32, b: i32) -> i32 {
a / b // ❌ b가 0이면 `panic!` 발생
}
fn main() {
let result = divide(10, 0); // ❌ panic 발생
println!("결과: {}", result);
}

이를 Result<T, E>를 사용해 Recoverable Error로 바꿀 수 있습니다
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("0으로 나눌 수 없습니다!".to_string()) // ❌ 에러 반환
} else {
Ok(a / b)
}
}
fn main() {
let result = divide(10, 0);
match result {
Ok(value) => println!("결과: {}", value),
Err(err) => println!("에러 발생: {}", err), // 🔥 panic 없이 에러 처리 가능
}
}
?연산자를 사용하면 에러를 전파할 수 있습니다
이를 통해 책임 분리가 가능합니다
- 값 처리는 compute_division()에서 처리
- 에러는 호출자에게 전파해서, 최종적으로 main()에서 처리
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("0으로 나눌 수 없습니다!".to_string())
} else {
Ok(a / b)
}
}
// `?` 연산자로 에러 전파
fn compute_division() -> Result<(), String> {
let result = divide(10, 0)?; // ❌ 에러 발생 시 즉시 반환
println!("결과: {}", result);
Ok(())
}
fn main() {
if let Err(err) = compute_division() {
println!("에러 발생: {}", err); // 🔥 여기서 에러 처리
}
}
'Rust' 카테고리의 다른 글
[Rust] 스마트 포인터 (2) - RefCell<T>, Arc<T>, Mutex<T> (0) | 2025.03.24 |
---|---|
[Rust] 스마트 포인터 (1) - Box<T>, Rc<T> (2) | 2025.03.24 |
[Rust] 제네릭(Generic), 트레잇(Trait), 동적 디스패처(dynamic dispatch) (2) | 2025.03.11 |
[Rust] 소유권, 참조와 빌림, 슬라이스 (0) | 2025.03.03 |
[Rust] 기초 문법 (2) | 2025.02.28 |