러스트에는 복구 가능한(recoverable) 에러와 복구 불가능한 (unrecoverable) 에러가 있다. recoverable 에러는 Result<T, E>
타입으로, unrecoverable 에러는 panic!
매크로로 처리한다.
panic!
코드가 실행되고 문제가 발생했지만 딱히 할 수 있는 일은 없을 수도 있다. 이런 panic 상황이 발생하면 실패 메시지를 출력하고, 되감고(unwind), 스택을 청소하고 종료한다.
panic!
매크로를 사용하면 즉시 panic을 발생시킨다.
fn main() {
panic!("crash!");
}
//thread 'main' panicked at crash
fn main() {
let v = vec![1, 2, 3];
v[99];
}
벡터 크기보다 큰 인덱스에 접근하려 하면 역시 panic이 발생한다. 에러의 원인이 정확히 무엇인지 확인하려면 코드 실행 시 RUST_BACKTRACE=1 cargo run
와 같이 백트레이스 환경변수를 설정하면 된다.
Result
enum Result<T, E> {
Ok(T),
Err(E),
}
Result
는 열거형이다. 성공한 경우 Ok
배리언트의 T 타입 값이 반환되고, 실패하면 Err
배리언트의 E 타입 값이 반환된다.
use std::fs::File;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}
File::open
은 Result<T, E>
를 반환한다. T
는 std::fs::File
로, E
는 std::io::Error
을 나타낸다.
Result
타입을 반환하므로, Result
의 배리언트 안에 있는 값을 꺼내서 사용해야 한다. 따라서 match
표현식을 사용해 처리한다.
파일이 성공적으로 열리면 file
값을 가져오고, 실패하면 panic!
메서드를 호출하도록 했다.
panic!
으로 처리하지 않고 경우에 따라 다르게 조치할 수 있도록 만들 수도 있다.
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => {
panic!("Problem opening the file: {:?}", other_error);
}
},
};
표준 라이브러리인 io
의 Error
구조체는 kind
메서드를 제공한다. 이를 호출하면 io::ErrorKind
열거형을 얻을 수 있다.
이 중 NotFound
배리언트인 경우, 파일이 없다는 뜻이므로 파일을 생성한다. 생성에 성공한 경우, 실패한 경우도 내부 match 문을 통해 처리해주어야 한다.
unwarp과 expect
unwrap
메서드는 만일 Result
값이 Ok
라면 Ok
내의 값을 반환하고, Err
라면 panic!
매크로를 호출해준다.
expect
는 panic!
에러 메시지도 선택할 수 있도록 해 준다.
use std::fs::File;
fn main() {
let greeting_file = File::open("hello.txt")
.expect("hello.txt should be included in this project");
}
에러 전파
함수 내에서 실패할 수도 있는 것을 호출할 때, 호출하는 코드 쪽으로 에러를 반환하는 propagating을 할 수도 있다.
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("hello.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}
?연산자를 사용하여 에러를 전파할 수 있다.
만약 값이 Err
라면 Err
값을 반환한다. 이 때 반환 타입은 io::Error
로 함수를 호출한 쪽에 넘어간다.
에러를 처리하는 커스텀 타입
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess { value }
}
pub fn value(&self) -> i32 {
self.value
}
}
new
는 Guess
값 인스턴스를 생성한다. 값이 1과 100 사이가 아니라면 panic을 호출한다.
이처럼 커스텀 타입을 만들어 에러를 처리하게 만들 수 있다.
'프로그래밍 > Rust' 카테고리의 다른 글
[Rust] 테스트 (0) | 2024.05.19 |
---|---|
[Rust] 제네릭과 트레이트 (0) | 2024.05.19 |
[Rust] 컬렉션 (0) | 2024.05.18 |
[Rust]프로젝트 모듈 관리 (0) | 2024.05.17 |
[Rust] 열거형 (0) | 2024.05.17 |