올바른 Rust 오류 처리 도구 선택: anyhow, thiserror, 또는 snafu?
Grace Collins
Solutions Engineer · Leapcell

Error handling 비용 처리는 Rust 개발에서 빼놓을 수 없는 부분입니다. Rust의 Result<T, E>
는 기본적인 지원을 제공하지만, 구체적인 구현은 시나리오에 따라 다릅니다. 이 글에서는 일반적으로 사용되는 세 가지 오류 처리 도구인 anyhow
, thiserror
, snafu
를 소개하고, 이들의 특징과 적용 가능한 시나리오를 분석하며, 실제 예제를 통해 프로젝트에서 이들을 사용하는 방법을 이해하도록 돕습니다. 애플리케이션을 개발하든 라이브러리를 작성하든, 이 글은 귀중한 참고 자료가 될 수 있습니다.
이 글은 Rust 오류 처리를 위한 세 가지 주요 도구를 심층적으로 다룹니다:
anyhow
는 빠르고 통합된 오류 처리에 적합하며, 애플리케이션 개발에 이상적입니다.thiserror
는 사용자 정의 오류 유형을 지원하여 라이브러리 개발에 적합합니다.snafu
는 컨텍스트 기반 오류 관리를 제공하며, 복잡한 시스템에 적합합니다.
이들의 강점과 약점을 비교하고 실제 코드 예제를 통해 프로젝트 요구 사항에 따라 적절한 도구를 선택하는 방법을 보여줍니다. 또한 Rust에서 오류를 보다 효과적으로 처리할 수 있도록 프로젝트 설정 단계와 예제 코드를 제공합니다.
Rust 오류 처리
오류 처리: anyhow
, thiserror
, snafu
anyhow
: 통합되고 간단한 오류 처리, 애플리케이션 수준 프로그래밍에 적합thiserror
: 사용자 정의되고 풍부한 오류 처리, 라이브러리 수준 프로그래밍에 적합snafu
: 더 세분화된 오류 관리
참고: 개발할 때 Result<T, E>
의 크기에 주의하세요.
anyhow
오류: 애플리케이션 수준 오류 처리
anyhow::Error
를 사용한 변환 및 통합 오류 처리:
std::error::Error
를 구현하는 모든 오류 유형을 지원하는 통합anyhow::Error
유형을 제공합니다.?
연산자를 사용하여 자동 오류 전파를 수행하여 다층 중첩 오류 처리를 간소화합니다.- 오류 가독성을 향상시키기 위해 동적 컨텍스트(
context()
메서드를 통해) 추가를 지원합니다.
fn get_cluster_info() -> Result<ClusterMap, anyhow::Error> { // Error 3: Err3 let config = std::fs::read_to_string("cluster.json")?; // Error 1: Err1 // let config = std::fs::read_to_string("cluster.json").context("...")?; // Error 1: Err1 let map: ClusterMap = serde_json::from_str(&config)?; // Error 2: Err2 Ok(map) } struct Err1 {...} struct Err2 {...} match ret { Ok(v) => v, Err(e) => return Err(e.into()) } Err1 => Err3: impl From<Err1> for Err3 Err2 => Err3: impl From<Err2> for Err3 impl From<Err1> for Err3 { fn from(v: Err1) -> Err3 { ... } }
thiserror
오류: 라이브러리 수준 오류 정의
- 매크로를 통해
std::error::Error
를 준수하는 오류 유형을 자동으로 생성합니다. - 중첩된 오류 소스(
#[from]
특성 사용) 및 구조화된 오류 정보를 지원합니다. - 오류 메시지 템플릿 사용자 정의를 허용합니다(예:
#[error("Invalid header: {expected}")]
).
참조: Rust std::error::Error trait documentation
Error
트레이트는 Debug
와 Display
를 모두 구현해야 하므로:
pub trait Error: Debug + Display {
다음과 같이 출력할 수 있습니다.
Error -> println!("{}/ {:?}", err)
snafu
오류: 컨텍스트 기반 오류 관리
Snafu
매크로를 통해 기본 오류를 도메인별 오류로 변환합니다.- 오류 체인에 구조화된 컨텍스트(예: 파일 경로, 입력 매개변수) 연결을 지원합니다.
- 조건 검사 및 오류 발생을 단순화하기 위해
ensure!
매크로를 제공합니다.
thiserror
vs snafu
자세한 내용은 다음을 참조하세요. kube-rs/kube discussion #453
비교 및 선택 가이드
차원 | anyhow | thiserror | snafu |
---|---|---|---|
오류 유형 | 통합 동적 유형 | 정적 사용자 정의 유형 | 도메인 기반 유형 |
컨텍스트 지원 | 동적 문자열 | 구조체 필드 | 구조화된 필드 + 동적 템플릿 |
적합한 단계 | 애플리케이션 개발(빠른 반복) | 라이브러리 개발(안정적인 인터페이스) | 복잡한 시스템(유지 보수성) |
학습 곡선 | 낮음(유형을 미리 정의할 필요 없음) | 중간(오류 구조 설계 필요) | 높음(컨텍스트 모델 이해 필요) |
일반적인 사용자 | 프런트엔드 개발자 / 스크립팅 도구 개발자 | 프레임워크 개발자 | 인프라 엔지니어 |
실제 구현
템플릿을 기반으로 프로젝트 rust-ecosystem-learning
생성 및 초기화
cargo generate --git git@github.com:qiaopengjun5162/rust-template.git cd rust-ecosystem-learning code .
프로젝트 디렉토리 구조
rust-ecosystem-learning on main [!] is 📦 0.1.0 via 🦀 1.85.0 via 🅒 base ➜ tree . -L 6 -I 'target|coverage|coverage_report|node_modules' . ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── _typos.toml ├── cliff.toml ├── deny.toml ├── docs └── src ├── error.rs ├── lib.rs └── main.rs 3 directories, 12 files
의존성 추가
cargo add anyhow cargo add thiserror cargo add serde_json
main.rs
파일
use anyhow::Context; use rust_ecosystem_learning::MyError; use std::fs; use std::mem::size_of; fn main() -> Result<(), anyhow::Error> { println!("size of anyhow::Error: {}", size_of::<anyhow::Error>()); println!("size of std::io::Error: {}", size_of::<std::io::Error>()); println!( "size of std::num::ParseIntError: {}", size_of::<std::num::ParseIntError>() ); println!( "size of serde_json::Error: {}", size_of::<serde_json::Error>() ); println!("size of string: {}", size_of::<String>()); println!("size of MyError: {}", size_of::<MyError>()); let filename = "non_existent_file.txt"; let _fd = fs::File::open(filename).with_context(|| format!("Cannot find file: {}", filename))?; fail_with_error()?; Ok(()) } fn fail_with_error() -> Result<(), MyError> { Err(MyError::Custom("This is a custom error".to_string())) } #[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }
lib.rs
파일
mod error; pub use error::MyError;
error.rs
파일
use thiserror::Error; #[derive(Error, Debug)] pub enum MyError { #[error("IO error: {0}")] Io(#[from] std::io::Error), #[error("Parse error: {0}")] Parse(#[from] std::num::ParseIntError), #[error("Serialize JSON error: {0}")] Serialize(#[from] serde_json::Error), // #[error("Error: {a}, {b:?}, {c:?}, {d:?}")] // BigError { // a: String, // b: Vec<String>, // c: [u8; 64], // d: u64, // }, #[error("Error: {0:?}")] BigError(Box<BigError>), #[error("An error occurred: {0}")] Custom(String), } #[derive(Debug)] pub struct BigError { pub a: String, pub b: Vec<String>, pub c: [u8; 64], pub d: u64, }
Cargo.toml
파일
[package] name = "rust-ecosystem-learning" version = "0.1.0" edition = "2021" license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.97" serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.140" thiserror = "2.0.11"
프로젝트 실행
rust-ecosystem-learning on main [!] is 📦 0.1.0 via 🦀 1.85.0 via 🅒 base took 2.9s ➜ cargo run Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/rust-ecosystem-learning` size of anyhow::Error: 8 size of std::io::Error: 8 size of std::num::ParseIntError: 1 size of serde_json::Error: 8 size of string: 24 size of MyError: 24 Error: Cannot find file: non_existent_file.txt Caused by: No such file or directory (os error 2)
요약
Rust의 오류 처리 도구는 각각 고유한 초점을 가지고 있습니다.
anyhow
는 간단하고 효율적이며, 애플리케이션 개발에 적합합니다.thiserror
는 구조화되고 명확하며, 라이브러리 설계에 적합합니다.snafu
는 풍부한 컨텍스트를 제공하여 복잡한 시나리오에 이상적입니다.
이 글의 분석 및 실제 예제를 통해 실제 요구 사항에 따라 적절한 오류 처리 솔루션을 선택할 수 있습니다. 적절한 오류 처리는 코드를 더욱 강력하게 만듭니다. 연습하고 Rust 프로젝트의 품질을 높이세요!
Leapcell은 Rust 프로젝트 호스팅을 위한 최고의 선택입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무제한 프로젝트를 무료로 배포하세요
- 사용량에 대해서만 지불하세요. 요청도 없고, 요금도 없습니다.
타의 추종을 불허하는 비용 효율성
- 유휴 요금 없이 사용한 만큼만 지불하세요.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
간편한 확장성 및 고성능
- 고도의 동시성을 쉽게 처리할 수 있도록 자동 확장됩니다.
- 운영 오버헤드가 전혀 없으므로 구축에만 집중하세요.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ