#Outcome 패턴 쿡북
버전: 0.43.0 | 업데이트: 2026-03-25 | 적용 대상: ranvier-core 0.42+ | 카테고리: 쿡북
#개요
Outcome<T, E>는 Ranvier의 제어 흐름 타입입니다. v0.42에서 컴비네이터와 try_outcome! 매크로가 도입되어 Result와 Outcome 간 변환 보일러플레이트가 크게 줄었습니다.
예제: outcome-patterns (examples/outcome-patterns/)
#1. `try_outcome!` 매크로
Result<T, E>를 조기 반환 Outcome::Fault로 변환합니다. 두 가지 형태:
#형태 1: 기본 변환
use ranvier_core::{prelude::*, try_outcome};
let pool = try_outcome!(bus.get_cloned::<PgPool>());
// Err 시: Outcome::Fault(e.to_string()) 반환#형태 2: 컨텍스트 문자열 포함
let pool = try_outcome!(bus.get_cloned::<PgPool>(), "PgPool이 Bus에 없음");
// Err 시: Outcome::Fault("PgPool이 Bus에 없음: <에러>") 반환#Before / After
// BEFORE (v0.41): 수동 match
let pool = match bus.get_cloned::<PgPool>() {
Ok(p) => p,
Err(e) => return Outcome::Fault(e.to_string()),
};
// AFTER (v0.42+): try_outcome!
let pool = try_outcome!(bus.get_cloned::<PgPool>());Import 주의:
try_outcome!은 prelude에 포함되지 않습니다.use ranvier_core::try_outcome;필요
#2. `Outcome::from_result()`
Result<T, E: Display> → Outcome<T, String> 변환:
let outcome: Outcome<i64, String> = Outcome::from_result("42".parse::<i64>());
// Ok(42) → Outcome::Next(42)
// Err(e) → Outcome::Fault(e.to_string())#3. 컴비네이터 체인: `and_then`
모나딕 바인드 — Outcome을 반환하는 클로저 체이닝:
let result = Outcome::<i64, String>::Next(10)
.and_then(|n| {
if n > 0 { Outcome::Next(n * 2) }
else { Outcome::Fault("양수여야 합니다".into()) }
})
.and_then(|n| Outcome::Next(format!("결과 = {n}")));
// Next("결과 = 20")Next가 아닌 변형(Branch, Jump, Emit, Fault)은 그대로 전달됩니다.
#4. `map`과 `map_fault`
성공 값 또는 에러 값 변환:
// map: Next 값 변환
let doubled = Outcome::<i64, String>::Next(5).map(|n| n * n);
// Next(25)
// map_fault: Fault 값 변환 (에러 타입 변환)
let typed_err = Outcome::<(), i32>::Fault(404)
.map_fault(|code| format!("HTTP {code}"));
// Fault("HTTP 404")#5. `unwrap_or`
Next 값을 추출하고 다른 변형에는 기본값 사용:
let value = some_outcome.unwrap_or(0);
// Next(42) → 42
// Fault(_) / Branch(_) / Jump(_) / Emit(_) → 0#6. 패턴: 컴비네이터를 활용한 파이프라인
async fn process_order(
&self, input: OrderInput, _res: &(), bus: &mut Bus,
) -> Outcome<OrderResult, String> {
let pool = try_outcome!(bus.get_cloned::<PgPool>(), "DB 사용 불가");
Outcome::from_result(
sqlx::query_as("SELECT * FROM orders WHERE id = $1")
.bind(input.order_id)
.fetch_one(&pool)
.await
.map_err(|e| e.to_string())
)
.and_then(|order: Order| {
if order.status == "cancelled" {
Outcome::Fault("주문이 이미 취소됨".into())
} else {
Outcome::Next(order)
}
})
.map(|order| OrderResult {
id: order.id,
total: order.total,
status: order.status,
})
}#7. 결정 매트릭스
| 상황 | 사용 |
|---|---|
Result → 조기 반환 Fault |
try_outcome!(expr) |
Result → 컨텍스트 포함 조기 반환 |
try_outcome!(expr, "컨텍스트") |
Result → 전체 Outcome 값 |
Outcome::from_result(expr) |
| 의존적 연산 체이닝 | .and_then(|v| ...) |
| 성공 값 변환 | .map(|v| ...) |
| 에러 타입 변환 | .map_fault(|e| ...) |
| 기본값으로 추출 | .unwrap_or(default) |
#참고
- Bus Access Patterns 쿡북 —
Bus::get_cloned(),BusHttpExt - JSON Outcomes 쿡북 — 라우트 경계에서의 타입이 지정된 JSON
examples/outcome-patterns/— 위 패턴 전체 실행 가능 데모