#Outcome Patterns Cookbook
Version: 0.43.0 | Updated: 2026-03-25 | Applies to: ranvier-core 0.42+ | Category: Cookbook
#Overview
Outcome<T, E> is Ranvier's control flow type. v0.42 introduced combinators and the try_outcome! macro to reduce boilerplate when converting between Result and Outcome. This cookbook covers all ergonomic patterns.
Example: outcome-patterns (in examples/outcome-patterns/)
#1. `try_outcome!` Macro
Converts Result<T, E> into early-return Outcome::Fault. Two forms:
#Form 1: Bare conversion
use ranvier_core::{prelude::*, try_outcome};
let pool = try_outcome!(bus.get_cloned::<PgPool>());
// On Err: returns Outcome::Fault(e.to_string())#Form 2: With context string
let pool = try_outcome!(bus.get_cloned::<PgPool>(), "PgPool not in Bus");
// On Err: returns Outcome::Fault("PgPool not in Bus: <error>")#Before / After
// BEFORE (v0.41): manual 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 Note:
try_outcome!is NOT in the prelude. Useuse ranvier_core::try_outcome;
#2. `Outcome::from_result()`
Converts Result<T, E: Display> into 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())Best for one-shot conversion when you need the full Outcome value (not early-return).
#3. Combinator Chain: `and_then`
Monadic bind โ chain Outcome-returning closures:
let result = Outcome::<i64, String>::Next(10)
.and_then(|n| {
if n > 0 {
Outcome::Next(n * 2)
} else {
Outcome::Fault("must be positive".into())
}
})
.and_then(|n| Outcome::Next(format!("result = {n}")));
// Next("result = 20")Non-Next variants (Branch, Jump, Emit, Fault) pass through unchanged.
#4. `map` and `map_fault`
Transform the success value or the error value:
// map: transform Next value
let doubled = Outcome::<i64, String>::Next(5).map(|n| n * n);
// Next(25)
// map_fault: transform Fault value (error type conversion)
let typed_err = Outcome::<(), i32>::Fault(404)
.map_fault(|code| format!("HTTP {code}"));
// Fault("HTTP 404")#5. `unwrap_or`
Extract the Next value with a default for any other variant:
let value = some_outcome.unwrap_or(0);
// Next(42) โ 42
// Fault(_) / Branch(_) / Jump(_) / Emit(_) โ 0#6. Pattern: Pipeline with Combinators
async fn process_order(
&self, input: OrderInput, _res: &(), bus: &mut Bus,
) -> Outcome<OrderResult, String> {
let pool = try_outcome!(bus.get_cloned::<PgPool>(), "DB unavailable");
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("Order already cancelled".into())
} else {
Outcome::Next(order)
}
})
.map(|order| OrderResult {
id: order.id,
total: order.total,
status: order.status,
})
}#7. Decision Matrix
| Situation | Use |
|---|---|
Result โ early-return Fault |
try_outcome!(expr) |
Result โ early-return with context |
try_outcome!(expr, "context") |
Result โ full Outcome value |
Outcome::from_result(expr) |
| Chain dependent operations | .and_then(|v| ...) |
| Transform success value | .map(|v| ...) |
| Transform error type | .map_fault(|e| ...) |
| Extract value with default | .unwrap_or(default) |
#See Also
- Bus Access Patterns Cookbook โ
Bus::get_cloned(),BusHttpExt - JSON Outcomes Cookbook โ typed JSON at route boundary
examples/outcome-patterns/โ runnable demo of all patterns above