#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.


  • Outcome Patterns Cookbook โ€” and_then, map_fault, combinators
  • Explicit Search Params Cookbook โ€” PageParams + search structs