Result 를 이용한 에러 핸들링
Result<T, E> 는 enum 타입이며, 주로 에러가 발생할 수 있는 함수에서 리턴 타입으로 사용됨.
보다시피 제너릭 타입임.
Result<T, E> 타입에는 Ok, Err 이라는 요소가 있음. Option<T> 때와 비슷하게 생각하면 됨. Ok 인 경우는 함수(또는 메서드)의 동작이 정상적으로 수행된 경우, Err 인 경우는 비정상적인 상황을 만나 함수가 정상적으로 수행되지 못했음을 알리는 것.
Ok 는 안에 정상적인 함수의 리턴값을 담고, Err 은 에러값을 담음.
예제로 보자면,
위 코드는 hello.txt 라는 파일을 찾아서 열기를 시도함. 그런데 hello.txt 파일이 존재하지 않아도 실행에 아무런 에러가 없음. 왜냐하면 File::open 함수는 Result<T, E> 를 리턴하기 때문. 바로 에러를 내고 프로그램이 죽어버리는 것이 아니라, 파일이 있을 경우 Ok 에 파일을 담아서 리턴하고, 파일이 없으면 Err 에 에러를 담아서 리턴하는 것. 사용자는 이걸 알고 경우에 맞는 처리를 하면 됨.
위 코드는 File::open 함수가 파일을 정상적으로 찾고 열었으면 변수 f 에 파일을 넣고, panic! 을 던지고 프로그램을 종료함.
실제로 파일이 없으므로 실행 결과는 아래와 같음.
에러 종류에 따른 처리
에러는 다양한 종류가 있을 수 있고, 에러의 종류에 따라 대응하는 코드가 달라질 수 있음. 긴 말이 필요없고, 아래 코드를 보면 됨.
읽기 시도하는 파일이 없을 경우 File::open 함수가 전달하는 에러는 std::io::ErrorKind 라는 enum 타입의 NotFound 요소임. 위와 같은 방법으로 에러의 종류에 따라 처리를 달리 할 수 있음.
if error.kind() == ErrorKind::NotFound 코드를 match guard 라 함. 그리고 Err(ref error) 의 ref 는 error 가 match guard 에 사용되면서도 오너십을 잃지 않게 하기 위함임. 이에 대해서는 후에 자세히 다룸.
에러 처리 코드 줄이기
자주 사용되어 반복되는 에러 처리 코드를 줄이는 방법이 있음. unwrap/expect 를 사용하는 것.
unwrap 은 Result 가 Ok 이면 Ok 안의 값을 리턴하고, Err 이면 알아서 panic! 을 호출함.
expect 는 unwrap 과 동일하게 동작하되, panic! 의 메시지를 다르게 지정할 수 있다는 차이만 있음.
에러 전파(propagating)
코드에서 에러가 발생할 경우 때로는 에러를 직접 처리하지 않고 그 코드를 호출한 코드에게 넘겨버리는게 좋을 때도 있음(자바에서 익셉션을 throw 할 때와 같이). 이럴 땐 다음과 같이 처리함.
읽을 파일이 없으면 더 이상 함수를 수행할 수 없으므로 에러를 그냥 던짐
보다시피, 당연히, 함수의 리턴 타입은 Result 가 와야 함. 위 함수는 에러가 발생하면 함수 호출 코드에게 에러 처리를 넘기고, 정상적으로 실행되면 파일의 내용을 스트링으로 리턴함(Ok(s))
에러 전파 코드 줄이기
에러 전파 코드는 자주 반복될 수 있음. '?' 를 사용해서 코드를 줄일 수 있음.
앞의 read_file 함수를 아래와 같이 줄일 수 있음.
위의 read_file 함수는 이전의 함수와 거의 똑같이 동작함. 차이점이 있다면, '?' 의 동작 방식임. '?' 는 std 라이브러리의 From 트레잇에 정의된 from 함수를 사용함.
코드 실행 중 에러가 발생하고 -> '?' 에 의해 from 함수가 호출되면 -> from 함수는 발생한 에러를 현재 함수의 리턴 타입에 맞는 에러로(예제에서는 io::Error로) 변환해줌. 이로 인해 한 함수가 여러가지 이유로 다른 에러가 발생해도 하나의 에러 타입으로 에러를 던질 수 있게 됨. 아무 에러나 이런 처리가 되는 것은 아니고, 에러 타입이 from 함수를 구현하여 이 에러가 어떻게 변환되어야 하는지 정의되어 있어야 함. 이 조건만 충족되면 '?' 는 알아서 에러를 변환해서 던져줌.
그리고 '?' 의 좋은 기능이 더 있음.
이전의 함수를 위와 같이 줄일 수 있음. File::open("hello.txt") 에서 에러가 발생하면 '?' 가 알아서 함수 실행을 중단하고 에러를 던지고, 정상적으로 파일이 열리면 파일을 리턴함. 그리고 그 파일에 바로 read_to_string 함수를 사용하는 것임. 제이쿼리나 여러가지 빌더 패턴에서 보는 체이닝 코드임.
'?' 는 반드시 리턴 타입이 Result 인 함수를 호출할 때만 사용할 수 있음. 왜냐하면 '?' 는 Result 타입을 다룰 때의 match 와 같은 방식으로 동작하기 때문임. match 를 Result 에 사용하되, Err 를 만나면 return Err(e) 를 수행하는 것이 '?' 의 동작 방식임. 때문에 리턴 타입이 Result 여야만 '?' 를 사용 가능. 아니면 컴파일 에러 발생.
'Rust' 카테고리의 다른 글
트레잇(Traits) (0) | 2018.02.28 |
---|---|
제너릭 타입 (0) | 2018.02.27 |
에러 핸들링(panic) (0) | 2018.02.26 |
컬렉션(해시맵) (0) | 2018.02.25 |
컬렉션(스트링) (0) | 2018.02.24 |