#Outcome::from_result Cookbook
Version: 0.44.0 | Updated: 2026-03-29 | Applies to: ranvier-core 0.42+ | Category: Cookbook
#Overview
Outcome::from_result() converts any Result<T, E> (where E: Display) into an Outcome<T, String>. This is the standard pattern for bridging external library errors (sqlx, serde_json, std::io) into Ranvier's Outcome type โ without adding framework-specific traits to those libraries.
#1. `Outcome::from_result` โ Basic Conversion
use ranvier_core::prelude::*;
// Any Result<T, E> where E: Display
let result: Result<Vec<User>, sqlx::Error> = sqlx::query_as("SELECT * FROM users")
.fetch_all(&*pool).await;
let outcome: Outcome<Vec<User>, String> = Outcome::from_result(result);
// Ok(users) โ Outcome::Next(users)
// Err(e) โ Outcome::Fault(e.to_string())#2. `Outcome::from_result_ctx` โ With Context Prefix
let outcome = Outcome::from_result_ctx(
sqlx::query_as("SELECT * FROM departments")
.fetch_all(&*pool).await,
"dept query failed"
);
// Err(e) โ Outcome::Fault("dept query failed: <error message>")#3. With `try_outcome!` โ Early Return
try_outcome! is for Result values where you want to early-return on error:
#[transition]
async fn get_dept(_: (), _: &(), bus: &mut Bus) -> Outcome<Department, String> {
let pool = try_outcome!(bus.get_cloned::<PgPool>(), "PgPool");
let id: String = try_outcome!(bus.path_param("id"), "path param");
let dept = try_outcome!(
sqlx::query_as("SELECT * FROM departments WHERE id = $1")
.bind(&id)
.fetch_optional(&*pool).await,
"dept query"
);
match dept {
Some(d) => Outcome::Next(d),
None => Outcome::Fault(format!("Department {id} not found")),
}
}#4. Decision Guide: `from_result` vs `try_outcome!`
| Scenario | Use | Example |
|---|---|---|
| Need the Outcome value for further processing | from_result |
let o = Outcome::from_result(...) |
| Want early return on error | try_outcome! |
let val = try_outcome!(expr, "ctx") |
| Chaining with Outcome combinators | from_result |
from_result(x).and_then(...) |
| Simple unwrap in transition body | try_outcome! |
Most transition code |
#5. Multiple Error Types
from_result works with any E: Display, not just sqlx:
// serde_json
let data = Outcome::from_result_ctx(
serde_json::from_str::<Config>(&raw),
"JSON parse"
);
// std::io
let content = Outcome::from_result_ctx(
std::fs::read_to_string("config.toml"),
"file read"
);
// reqwest
let response = Outcome::from_result_ctx(
reqwest::get("https://api.example.com/data").await,
"HTTP call"
);#6. Why Not `IntoOutcome` Trait?
A trait on Result would enable chaining: db_call.into_outcome(). We chose wrapping instead:
| Pattern | Code | Visibility |
|---|---|---|
Outcome::from_result(expr) |
Wrapping | Conversion point is explicit |
expr.into_outcome() |
Chaining | Conversion hidden behind trait |
The wrapping pattern makes it clear where the Result โ Outcome boundary is crossed. Also, from_result works with any E: Display universally โ no need for blanket trait implementations that could conflict with other crates.
#Related
- Outcome Patterns Cookbook โ
and_then,map_fault, combinators - Explicit Search Params Cookbook โ
PageParams+ search structs