상세 컨텐츠

본문 제목

21. Rust 오류 처리

Rust를 처음부터 배우세요

by 러스트코리아 2024. 12. 21. 01:36

본문

반응형

사람은 누구나 실수를 하며, 프로그래밍 사업도 예외는 아닙니다.

모든 실수는 되돌릴 수 있고, 되돌릴 수 없는 실수도 있지만, 되돌릴 수 없는 실수는 영구적인 트라우마가 됩니다.

프로그래밍에는 두 가지 종류의 오류가 있습니다. 한 종류의 오류는 잡아서 쉽게 처리할 수 있으며, 다른 종류의 오류는 관련이 없으며 프로그램이 중단되고 종료될 수 있습니다.

Rust 언어에도 오류 개념이 있으며 오류는 복구 가능  복구 불가능이라는 두 가지 범주로 구분됩니다. 이는 다른 언어의 예외  오류 와 동일합니다 .

아래는 예시와 같습니다.

이름 설명 예시
복구 가능 잡을 수 있습니다. 다른 언어의 Exception과 동일합니다. result()
복구 불가능 캡처할 수 없으며 프로그램이 충돌하고 종료됩니다. panic!()

 

복구 가능

  • 복구 가능한 오류는 포착할 수 있는 오류이므로 수정이 가능하고 프로그램이 계속 실행될 수 있습니다. 복구 가능한 오류가 발견되면 프로그램은 이전에 실패한 작업을 계속 시도하거나 대체 작업을 선택할 수 있습니다.
  • 복구 가능한 오류의 일반적인 예는 존재하지 않는 파일을 읽을 때 발생하는 파일을 찾을 수 없음 오류입니다.

복구할 수 없음

  • 복구할 수 없는 오류는 프로그램 충돌을 일으키는 치명적인 오류입니다. 이러한 오류가 발생하면 프로그램이 즉시 중지됩니다.
  • UnRecoverable 오류의 일반적인 예는 범위를 벗어난 배열입니다.

다른 언어와 달리 Rust 언어에는 예외 개념이 없지만 복구 가능한 오류 가 있습니다 .

Rust 언어는 복구 가능한 오류가 발생하면 결과를 반환합니다.

복구할 수 없는 오류가 발생하면panic() 매크로가 자동으로 호출됩니다.

panic!() 매크로로 인해 프로그램이 즉시 종료될 수 있습니다.

21.1 panic!() 매크로 및 복구할 수 없는 오류

panic!()은 프로그램이 즉시 종료되도록 하고 프로그램이 종료될 때 호출자에게 종료 이유를 보고합니다.

Panic!() 매크로의 구문 형식은 다음과 같습니다.

  1. panic!( string_error_msg )

String_error_msg는 문자열 형태로 호출자에게 프로그램 종료 이유를 전달하는데 사용됩니다.

정상적인 상황에서 복구할 수 없는 오류가 발생하면 프로그램은 자동으로 panic!()을 호출합니다.

그러나 프로그램을 종료하기 위해 수동으로 panic!()을 호출할 수도 있습니다.

되돌릴 수 없는 오류가 발생하지 않는 한 panic!()을 사용하지 마세요.

21.1.1  예제 1

다음 예에서는 panic!()으로 인해 프로그램이 즉시 종료되므로 후속 println!() 매크로가 실행되지 않습니다.

  1. fn main() {
  2. panic!("Hello");
  3. println!("End of main"); // 실행되지 않음
  4. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. thread 'main' panicked at 'Hello', main.rs:3

21.1.2  예제 2: 배열이 범위를 벗어나는 오류

다음 코드는 배열의 최대 첨자가 10보다 훨씬 작은 2이기 때문에 배열 범위를 벗어나는 오류를 발생시킵니다.

  1. fn main() {
  2. let a = [10,20,30];
  3. a[10]; // 최대 첨자가 10보다 훨씬 작은 2
  4. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. warning: this expression will panic at run-time
  2. --> main.rs:4:4
  3. |
  4. 4 | a[10];
  5. | ^^^^^ index out of bounds: the len is 3 but the index is 10
  6.  
  7. $main
  8. thread 'main' panicked at 'index out of bounds: the len
  9. is 3 but the index is 10', main.rs:4
  10. note: Run with `RUST_BACKTRACE=1` for a backtrace.

21.1.3  예제 3: 프로그램이 종료되도록 하기 위해 수동으로 panic!()을 시작합니다.

프로그램 실행 중에 확립된 비즈니스 규칙을 위반하는 경우, panic!() 매크로를 수동으로 호출하여 프로그램을 종료할 수 있습니다.

예를 들어, 다음 코드는 13이 홀수이기 때문에 짝수를 요구하는 규칙을 위반합니다.

  1. fn main() {
  2. let no = 13;
  3. // 패리티 테스트
  4. if no % 2 == 0 {
  5. println!("Thank you , number is even");
  6. } else {
  7. panic!("NOT_AN_EVEN");
  8. }
  9. println!("End of main");
  10. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. thread 'main' panicked at 'NOT_AN_EVEN', main.rs:9
  2. note: Run with `RUST_BACKTRACE=1` for a backtrace.

21.2  결과 열거 및 복구 가능한 오류

C와 같은 일부 오래된 언어는 전역 변수 errno를 설정하여 발생한 오류를 프로그램에 알려주는 반면, Java와 같은 다른 언어는 프로그램 복구 목적을 달성하기 위해 반환 유형을 기반으로 포착 가능한 예외를 지정합니다. Go와 같은 경우 오류와 정상값을 함께 반환하여 복구성을 달성합니다.

Rust는 복구 가능한 오류(Recoverable)에 대해 더 굵게 표시합니다. Result 열거형을 사용하여 일반 반환 값과 오류 정보를 캡슐화합니다. 전역 공간을 오염시키지 않고 하나의 변수만 정상 값과 오류 정보를 받을 수 있다는 장점이 있습니다.

결과열거형은 복구 가능한 오류를 처리하도록 설계되었습니다.

결과 열거형은 다음과 같이 정의됩니다.

  1. enum Result<T,E> {
  2. OK(T),
  3. Err(E)
  4. }

결과열거형에는 OK와 Err라는 두 가지 값이 포함되어 있습니다.

T와 E는 두 가지 일반 매개변수입니다.

  • T는 Result의 값이 OK일 때 정상적으로 반환되는 값으로 사용되는 데이터 타입이다.
  • E Result가 Err일 때 오류로 반환되는 오류 유형입니다.

21.2.1  예제 1: 결과 열거의 간단한 사용

다음 예제에서는 존재하지 않는 파일을 열어 결과 열거형을 사용하는 방법을 보여줍니다.

  1. use std::fs::File;
  2. fn main() {
  3. let f = File::open("main.jpg"); // 파일이 존재하지 않으므로 값은 Result.Err입니다.
  4. println!("{:?}",f);
  5. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. Err(Error { repr: Os { code: 2, message: "No such file or directory" } })

위 코드에서 main.jpg가 존재하면 결과는 OK(파일)입니다. 파일이 존재하지 않으면 결과는 Err(오류)입니다.

위 코드는 오류 정보만 출력합니다. 이는 단지 데모용입니다. 일반적인 상황에서는 결과 유형에 따라 다른 선택을 해야 합니다.

21.2.2  예 2: 오류 정보 캡처 및 프로그램 작동 재개

다음 코드에서는 match를 사용하여 Result의 다양한 값을 다르게 처리합니다.

  1. use std::fs::File;
  2. fn main() {
  3. let f = File::open("main.jpg"); // main.jpg 파일이 존재하지 않습니다
  4. match f {
  5. Ok(f)=> {
  6. println!("file found {:?}",f);
  7. },
  8. Err(e)=> {
  9. println!("file not found \n{:?}",e); // 오류 처리
  10. }
  11. }
  12. println!("end of main");
  13. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. file not found
  2. Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
  3. end of main

참고: 위 코드에서는 f 값에 관계없이 최종 println!(“end of main”)이 실행됩니다.

21.2.3  예시 3: 실습 함수가 오류를 반환함

다음 코드에서는 is_even() 함수를 정의합니다. 전달된 인수가 짝수가 아닌 경우 복구 가능한 오류가 발생합니다.

  1. fn main(){
  2. let result = is_even(13);
  3. match result {
  4. Ok(d)=>{
  5. println!("no is even {}",d);
  6. },
  7. Err(msg)=>{
  8. println!("Error msg is {}",msg);
  9. }
  10. }
  11. println!("end of main");
  12. }
  13. fn is_even(no:i32)->Result<bool,String> {
  14. if no%2==0 {
  15. return Ok(true);
  16. } else {
  17. return Err("NOT_AN_EVEN".to_string());
  18. }
  19. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. Error msg is NOT_AN_EVEN
  2. end of main

21.3 unwrap() 함수와 Expect() 함수

위의 결과, match 문을 사용하는 것이 꽤 좋아 보이지만 너무 많이 쓰면 Go 언어처럼 if err != nil이 하늘을 날아다닐 것입니다.

때로는 Err을 처리하고 싶지 않거나 프로그램이 Err을 자체적으로 처리하도록 하고 싶지 않을 때도 있습니다. 때로는 특정 OK 값만 필요할 수도 있습니다.

이러한 두 가지  요구에 대응하여 Rust 언어 개발자는 표준 라이브러리에 두 가지 도우미 함수 unwrap()  Expect() 를 정의했습니다 .

 

함수명 코드 설명
unwrap unwrap(self):T self가 Ok 또는 Some이면 포함된 값이 반환됩니다.
그렇지 않으면 매크로panic!()이 호출되고 프로그램은 즉시 종료됩니다.
expect expect(self,msg:&str):T self가 Ok 또는 Some이면 포함된 값이 반환됩니다.
그렇지 않은 경우에는 panic!()을 호출하여 사용자 정의된 오류를 출력하고 종료합니다.

Expect() 함수는 실패를 원하지 않는 오류 상황을 단순화하는 데 사용됩니다.

unwrap() 함수는 OK가 성공적으로 반환될 때 반환되는 실제 결과를 추출합니다.

unwrap() 및 Expect()는 결과만 처리할 수 없습니다.열거형을 사용하여 옵션을 처리할 수도 있습니다.

21.4 unwrap() 함수

unwrap() 함수는 성공적인 작업의 실제 결과를 반환합니다. 작업이 실패하면 panic!()을 호출하고 기본 오류 메시지를 인쇄합니다.

unwrap() 함수의 프로토타입은 다음과 같습니다.

  1. unwrap(self):T

실제로 unwrap() 함수의 내부 구현은 위에서 본 match 문입니다.

21.4.1 예 1

다음 코드에서는 unwrap() 함수를 사용하여 짝수를 판단하는 예를 변형해 보겠습니다. 훨씬 더 편해 보이지 않나요?

  1. fn main(){
  2. let result = is_even(10).unwrap();
  3. println!("result is {}",result);
  4. println!("end of main");
  5. }
  6. fn is_even(no:i32)->Result<bool,String> {
  7. if no%2==0 {
  8. return Ok(true);
  9. } else {
  10. return Err("NOT_AN_EVEN".to_string());
  11. }
  12. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. result is true
  2. end of main

21.4.2 예제 2

is_even(10)에 전달된 10을 13으로 변경하면 프로그램이 종료되고 오류 메시지가 출력됩니다.

  1. fn main(){
  2. let result = is_even(10).unwrap();
  3. println!("result is {}",result);
  4. println!("end of main");
  5. }
  6. fn is_even(no:i32)->Result<bool,String> {
  7. if no%2==0 {
  8. return Ok(true);
  9. } else {
  10. return Err("NOT_AN_EVEN".to_string());
  11. }
  12. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. thread 'main' panicked at 'called `Result::unwrap()` on
  2. an `Err` value: "NOT_AN_EVEN"', libcore\result.rs:945:5
  3. note: Run with `RUST_BACKTRACE=1` for a backtrace

21.5 함수 Expect()

함수 Expect()는 self가 Ok 또는 Some일 때 포함된 값을 반환합니다. 그렇지 않은 경우에는 panic!()을 호출하여 사용자 정의 오류를 출력하고 프로그램을 종료하십시오.

함수 Expect()의 프로토타입은 다음과 같습니다.

  1. expect(self,msg:&str):T

함수 Expect()는 unwrap()과 동일합니다. 유일한 차이점은 오류가 발생할 때 Expect()가 기본 오류 메시지 대신 사용자 정의 오류 메시지를 출력한다는 것입니다.

21.5.1 예

다음 코드에서는 파일이 존재하지 않는 위의 일치 예제를 변환하기 위해 Expect() 함수를 사용합니다.

  1. use std::fs::File;
  2. fn main(){
  3. let f = File::open("pqr.txt").expect("File not able to open"); // 파일이 존재하지 않습니다
  4. println!("end of main");
  5. }

위의 Rust 코드를 컴파일하고 실행하면 출력 결과는 다음과 같습니다.

  1. thread 'main' panicked at 'File not able to open: Error { repr: Os
  2. { code: 2, message: "No such file or directory" } }', src/libcore/result.rs:860
  3. note: Run with `RUST_BACKTRACE=1` for a backtrace.
현재 콘텐츠 저작권은 chapin666 또는 그 계열사에 있습니다.

 

반응형

'Rust를 처음부터 배우세요' 카테고리의 다른 글

23. Rust IO  (0) 2024.12.21
22. Rust 제네릭  (0) 2024.12.21
20. Rust 컬렉션  (0) 2024.12.14
19. Rust 모듈  (0) 2024.11.27
18. Rust 열거 Enum  (0) 2024.11.20

관련글 더보기