Rust 기초부터 마스터까지 — 7화
Result와 모듈, Cargo로 오류와 프로젝트 관리하기
본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.
Rust는 복구 가능한 실패를 Result로 표현한다. 프로젝트가 커지면 오류 타입과 함께 모듈 경계, Cargo 설정, 테스트도 코드 계약의 일부가 된다.
Result와 물음표 연산자
use std::fs;
use std::io;
fn load_config(path: &str) -> Result<String, io::Error> {
let text = fs::read_to_string(path)?;
Ok(text)
}
?는 성공 값이면 안쪽 값을 꺼내고 실패면 현재 함수에서 오류를 반환한다. 오류를 무시하는 문법이 아니라 호출자에게 전달하는 짧은 표현이다.
오류에 문맥 추가하기
fn parse_level(text: &str) -> Result<u32, String> {
text.trim()
.parse::<u32>()
.map_err(|error| format!("invalid level: {error}"))
}
라이브러리는 호출자가 처리할 수 있도록 오류를 반환하고, 프로그램 경계에서 로그나 사용자 메시지로 바꿀 수 있다. 예상 가능한 입력 오류에 panic을 사용하면 복구 기회를 잃는다.
모듈로 공개 범위 정하기
mod combat {
pub struct Damage(pub u32);
pub fn calculate(attack: u32, defense: u32) -> Damage {
Damage(attack.saturating_sub(defense).max(1))
}
}
기본 항목은 모듈 밖에 공개되지 않고 pub을 붙인 계약만 사용할 수 있다. 내부 구현을 숨기면 변경이 다른 모듈로 퍼지는 범위를 줄일 수 있다.
Cargo와 테스트
#[cfg(test)]
mod tests {
use super::combat;
#[test]
fn damage_is_at_least_one() {
assert_eq!(combat::calculate(5, 100).0, 1);
}
}
Cargo는 의존성, 빌드, 테스트, 문서 생성을 하나의 프로젝트 규칙으로 관리한다. 오류 타입과 모듈 공개 범위를 작게 유지하면 사용하는 쪽이 알아야 할 계약도 작아진다.