Rust 오류 처리에 빠진 한 조각
요약
본 글은 Rust에서 발생하는 복잡하고 다양한 오류 처리 문제를 해결하는 새로운 크레이트(eros)에 대해 논합니다. 특히, 여러 개의 독립적인 오류를 한 번에 반환하거나 특정 호출 지점의 구체적인 오류 타입을 명시적으로 다루는 방법을 제시하며 개발자들의 관심을 받고 있습니다.
핵심 포인트
- 여러 오류를 동시에 반환하는 문제를 해결할 수 있는 구조적 접근을 제공합니다.
- 오류 처리를 컴파일 타임 타입 태그와 연결하여 정확성을 높입니다.
- 저수준 오류(예: ParseIntError)는 의미가 부여된 구체적인 타입으로 감싸서 처리해야 합니다.
- 개발자는 호출자가 필요한 오류 타입에만 집중하도록 설계하는 것이 중요합니다.
모듈 전체의 무관한 오류까지 들어 있는 열거형 대신, 해당 호출에서 실제로 발생할 수 있는 오류 집합을 정의할 수 있어 좋음. 오류 타입을 다루며 겪었던 답답함을 해결해 줄 듯해서 직접 써 보고 싶음.
또 하나 불편했던 부분은 여러 오류를 한꺼번에 반환하기임. 백엔드 CRUD 애플리케이션에서 생성 요청의 필드를 검증할 때, 제약을 위반한 필드를 하나만이 아니라 모두 반환하고 싶지만 Result와 ?는 조기 반환 때문에 다루기 까다로움. 오류 벡터를 사용하는 크레이트도 봤지만 그다지 우아하지 않았는데, 이 크레이트의 구조에서 해결책의 실마리를 찾을 수 있을지 궁금함.
훌륭해 보이는데 단점은 없을까? 기존 코드를 호출할 때 오류를 이 방식으로 변환해 주는 anyhow 어댑터도 있으면 좋겠음.
.widen()이나 .narrow()를 쓰려면 타입을 특정 순서로 나열해야 한다는 점이 단점일 줄 알았는데, 순서와 무관하게 작동하는 듯함. eros 에는 오류를 실제로 순서 없는 집합으로 만드는 기법이 여럿 들어 있는 듯함.
다만 컴파일러 오류 메시지가 단점일 수는 있겠음. 직접 시험하지는 않았지만 같은 타입을 두 번 지정하거나 오류가 아닌 타입을 넣는 등 잘못된 조합에서 어떨지 궁금함. 어쨌든 this가 어떻게 작동하는지 설명하는 블로그 글을 읽고 싶음.
OCaml은 다형적 변형 타입(polymorphic variants) 덕분에 이런 오류 처리 방식을 기본으로 지원함. 직접 써 보니 오류 집합의 합집합을 쉽게 만들 수 있으면 그 방식에 의존하게 되는 것이 단점임. 프로그래머가 멈춰서 오류를 명시적으로 설계할 계기가 없어져, 유용한 추가 맥락을 담지 못한 빈약한 설계로 끝나기 쉬움.
제대로 이해했다면 본질적으로 컴파일 타임 타입 태그를 붙인 anyhow 임. 트레이트 해석이 꽤 어려워 보여서, 유일하게 걱정되는 부분은 컴파일에 걸리는 시간임.
제작자임. 내 생각에 유일한 단점은 alloc 없는 no_std 환경에서는 작동하지 않는다는 것임. 하지만 이것도 동료에게 영감을 받아 방금 해결 방법을 찾았고, 며칠 안에 PR을 올릴 수 있기를 바람. 세부 구현과 조건부 컴파일을 제대로 맞춰야 하지만 API는 그대로 유지할 예정임. eros의 API는 개발자에게 최대한의 유연성을 주도록 세심하게 설계했고, 결과에 매우 만족함. anyhow 어댑터는 이미 anyhow 기능 플래그 뒤에 마련되어 있음. https://github.com/mcmah309/eros#anyhow.
이런 식으로 ParseIntError를 노출하는 것은 미심쩍음. 단순히 아무 정수나 파싱하다 실패한 것이 아니라, 구체적으로 포트 파싱에 실패한 것이기 때문임. Redis 접속 주소처럼 포트와 데이터베이스 번호라는 두 정수가 들어간다면 ParseIntError만으로는 어느 쪽인지 알 수 없음. 그러면 ?나 자동 확장만으로 처리할 수 없고, .map_err(ParsePortError) 같은 코드를 직접 써서 구별해야 함. 프로그램에서 구별하려면 타입이 달라야 하므로, 타입을 바꾸지 않는 #[context]만으로는 부족함.
그래서 From<ParseIntError> for ConnectionError 같은 구현은 안티패턴이라고 봄. 이런 저수준 오류는 의미가 드러나는 타입이나 변형으로 감싸는 편이 좋음. 예를 들면 ConnectionError::ParsePort(ParseIntError)와 ConnectionError::ParseDb(ParseIntError)로 나눌 수 있음. 이를 지원하는 장치가 있으면 좋겠지만, 매크로를 대거 사용하지 않고 가능한지는 모르겠음.
애초에 내부 오류를 노출해야 하는지도 별도로 고려해야 함. 나중에 Redis가 이름 기반 데이터베이스로 전환한다면, 처음부터 내부 오류를 불투명하게 두는 편이 나았다고 느낄 수 있음.
그 예시는 최선이 아니었고, 주로 eros의 처리 흐름과 API를 보여 주기 위한 것이었음. 실제 코드에서는 호출자가 그 정확한 오류 타입에 관심이 있을 때만ParseIntError를 노출해야 함. README의 첫 번째 철학도 “오류 타입은 호출자가 그 타입을 중요하게 여길 때만 의미가 있으며, 그렇지 않으면 사용성을 해치고 불필요한 잡음만 만든다”는 것임.
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기