[RUST] 러스트 프로그래밍 공식 가이드(제2판) 9장 요약
에러처리
- 러스트에는 예외 처리 기능이 없음
- 러스트 에러의 종류
- 복구 가능한(recoverable) 에러
Result<T, E>
타입
- 복구 불가능한(unrecoverable) 에러
- 프로그램을 종료하는 panic! 매크로
- 복구 가능한(recoverable) 에러
panic!으로 복구 불가능한 에러 처리하기
- 패닉 : 프로그램이 더 이상 정상적으로 실행될 수 없는 심각한 오류 상황에서 발생
- 실패 메시지를 출력
- 되감기(unwind)
- 러스트가 패닉을 발생시킨 함수로부터 스택을 거꾸로 훑어가면서 데이터를 청소한다는 의미
- 데이터 정리 작업 없이 즉각 종료되는 그만두기(aborting)을 하고 싶다면 아래와 같이 설정
- Cargo.toml
1 2
[profile.XXX] // XXX 는 dev 또는 release 가 될 수 있음 panic = 'abort'
- Cargo.toml
- 스택을 청소
- 프로세스 종료
- 패닉을 일으키는 두 가지 방법
- 패닉을 일으킬 동작을 하는 것 (예: 배열 끝 부분을 넘어선 접근)
panic!
매크로를 명시적으로 호출
panic! 백트레이스 이용하기
- 백트레이스란?
- 패닉(panic) 발생 시 어떤 경로로 함수들이 호출되다가 에러가 발생했는지 보여주는 호출 스택 기록을 의미
- 즉, “에러가 어디서 발생했는지”, “그 지점까지 어떤 경로를 거쳤는지” 알 수 있는 디버깅 도구
- 백트레이스 출력하기
RUST_BACKTRACE=1 cargo run
와 같이 환경변수RUST_BACKTRACE
를 켜주면 스택 추적이 자세히 나옴
Result로 복구 가능한 에러 처리하기
- 복구 가능한에러는
Result
열거형으로 표현Result<T, E>
타입- 배리언트
Ok(T)
: 성공 시 값 반환Err(E)
: 실패 시 에러 반환- 제네릭 타입이라서, 성공값 타입(
T
) 와 오류 타입(E
)을 원하는 타입으로 정할 수 있음
- 처리 방식
match
구문으로 분기 처리Ok
: 성공에 대한 처리Err
: 에러에 대한 처리
- 배리언트
서로 다른 에러에 대해 매칭하기
다른 종류의 에러를 다른 방식으로 처리하기
File::Open
이 반환하는Err
배리언트 값의 타입은io::Error
구조체io::Error
구조체는 kind 메서드를 지원 (io::ErrorKind
를 얻을 수 있음)io::ErrorKind
는 io 연산으로부터 발생할 수 있는 다양한 종류의 에러를 나타내는 배리언트가 있는 열거형- 즉,
error.kind()
에 대한 내부 매칭을 할 수 있음1 2 3 4
let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => match error.kind() {} // 파일이 존재하지 않을때에 대한 처리
- 위와 같이
match
표현식은 매우 용하지만 매우 원시적이어서 너무 많은match
가 생길 수 있음
에러 발생 시 패닉을 위한 숏컷: unwrap과 expect
match
의 사용은 충분히 잘 작동하지만, 코드가 장황하기도 하고 의도를 항상 잘 전달하는 것은 아님, 따라서Result<T,E>
는 다양한 특정 작업을 수행하기 위한 도우미 메서드 들을 제공match
구문과 비슷한 구현을 한 숏컷 메서드unwrap()
Result
값이 Ok 배리언트라면, 값을 반환Result
값이 Err 배리언트라면panic!
매크로를 호출로 기본 메시지 출력
expect()
( 권장 )Result
값이 Ok 배리언트라면, 값을 반환Result
값이 Err 배리언트라면panic!
매크로를 호출로 매개변수로 전달한 에러 메시지 출력
에러 전파하기
- 에러 전파하기 : 어떠한 함수에서 에러가 발생했을 때 에러를 함수 안에서 처리는 대신 이 함수를 호출하는 코드 쪽으로 에러를 반환하여 그쪽에서 수행할 작업을 결정하도록 하는 것을 말함
- 예로
Result
를 반환하여 성공 또는 에러 발생시에 대한 처리를 함수 호출부에서 선택할 수 있도록 할 수 있음match
의Err(e)
갈래에서 returnErr(e)
를 하는 방식
- 예로
에러를 전파하기 위한 숏컷: ? 연산자
Result<T, E>
타입을 반환하는 함수에서 사용할 수 있음Result
의 값이 Ok라면,Ok
안의 값이 얻어짐Result
의 값이 Err라면,Err
의 값이 반환
match
표현식과?
연산자의 차이점?
연산자를 사용할 때의 에러값은from
함수를 거침from
함수는 표준 라이브러리 내의From
트레이트에 정의- 어떤 값의 타입을 다른 타입으로 변환하는데 사용
?
연산자가from
함수를 호출하면,?
연산자가 얻게되는 에러를?
연산자가 사용된 현재 함수의 반환 타입에 정의된 에러타입으로 변환
File::open("test.txt")?.read_to_string(&mut username)?;
과 같이 호출을 연결하여 사용할 수 있음- 만약
File::open("test.txt")?
에서 에러가 발생하면 read_to_string 은 실행되지 않고, 파일 열기에 대한Err(e)
를 반환
- 만약
?
연산자는 어떤 함수가 다양한 종류의 에러로 인해 실패할 수 있지만, 모든 에러를 하나의 에러 타입으로 반환할 때 유용?
연산자는 많은 양의 보일러플레이트(match
,if let
)를 제거해주고 함수의 구현을 더 단순하게 만들어줌?
연산자가 사용될 수 있는 곳?
는?
가 사용된 값과 호환 가능한 반환 타입을 가진 함수에서만 사용될 수 있음Result<T, E>
또는Option<T>
에서 사용할 수 있음?
는 오류가 발생했을 때 함수에서 조기에Err(...)
혹은None
을 반환하는 역할을 함
Panic!이냐, panic!이 아니냐, 그것이 문제로다
예제, 프로토타입 코드, 그리고 테스트
- 에러를 어떻게 처리할지 결정할 준비가 되기 전이라면,
unwrap
과expect
메서드가 프로토타이핑을 할 떄 매우 편리 - 테스트 내에서 메서드 호출이 실패한다면, 해당 메서드가 테스트 중인 기능이 아니더라도 전체 테스트를 실패시키도록 함
여러분이 컴파일러보다 더 많은 정보를 가지고 있을 때
Result
가Ok
값을 가지고 있을 거라 확신할 만한 논리적 근거가 있지만, 컴파일러가 그 논리를 이해할 수 없는 경우라면,unwrap
혹은expect
를 호출하는 것이 적절할 수 있음
에러 처리를 위한 가이드라인
- 복구 가능한 에러는 Result로 처리
- 복구 불가능한 에러는 panic! 으로 처리
유효성을 위한 커스텀 타입 생성하기
- 새로운 타입을 만들어서 그 타입의 인스턴스를 생성하는 함수에서 유효성을 확인하는 방식으로 유효성 확인을 모든 곳에서 반복하지 않게 할 수 있음
- 함수가 새로운 타입을 시그니처에 사용하여 받은 값을 자신있게 사용할 수 있어 안전
This post is licensed under CC BY 4.0 by the author.