Skip to main content

MisconfigurationStrategy

Trait MisconfigurationStrategy 

Source
pub trait MisconfigurationStrategy {
    type Error<E>;

    // Required methods
    fn fallback<T, E>(
        &self,
        result: Result<T, E>,
        fallback_fn: impl FnOnce(E) -> T,
    ) -> Result<T, Self::Error<E>>;
    fn fallback_opt<T, E>(
        &self,
        result: Result<T, E>,
        fallback_fn: impl FnOnce(E),
    ) -> Result<Option<T>, Self::Error<E>>;
    fn to_anyhow<T, E>(
        &self,
        result: Result<T, Self::Error<E>>,
    ) -> Result<T, Self::Error<Error>>
       where Error: From<E>;
    fn map_err<T, E1, E2>(
        &self,
        result: Result<T, Self::Error<E1>>,
        map_err: impl FnOnce(E1) -> E2,
    ) -> Result<T, Self::Error<E2>>;
}
Expand description

Generic handling of two possible approaches to an Error:

Any function that wants to be made generic over these approaches should be changed thusly.

Old:

fn do_thing()
    -> Result<T, E>
{
    let x = something_fallible()?;
    Ok(x)
}

New:

fn do_thing<Strategy: MisconfigurationStrategy>(strategy: &Strategy)
    -> Result<T, Strategy::Error<E>>
{
    let x = strategy.fallback(something_fallible(), |err| {
        tracing::debug!("Failed to get value: {err}");
        MyType::default()
    })?;
    Ok(x)
}

The key trick is instead of returning Result<T, E> your function should return Result<T, Strategy::Error<E>>. Which simplifies to:

Notably, if your function returns Result<T, Strategy::Error<E>> you will be statically prevented from returning an Err without going through MisconfigurationStrategy::fallback or MisconfigurationStrategy::fallback_opt which ensure you’re handling both approaches (or you wrote an unwrap but those standout far more than adding a new ? to a function that must be able to Not Fail).

Also, for any caller that passes in UseDefaultStrategy, they will be able to write let Ok(val) = do_thing(&UseDefaultStrategy); instead of having to write an unwrap().

Required Associated Types§

Required Methods§

Source

fn fallback<T, E>( &self, result: Result<T, E>, fallback_fn: impl FnOnce(E) -> T, ) -> Result<T, Self::Error<E>>

Try to get the value out of a Result that we need to proceed.

If UseDefaultStrategy, on Err this will call fallback_fn to compute a default value and always return Ok.

If FallibleStrategy this is a no-op and will return the Result.

Source

fn fallback_opt<T, E>( &self, result: Result<T, E>, fallback_fn: impl FnOnce(E), ) -> Result<Option<T>, Self::Error<E>>

Try to get the value out of a Result that we can do without.

If UseDefaultStrategy, this will call fallback_fn to report an issue (i.e. you can invoke tracing::debug! or something) and then return None.

If FallibleStrategy this is a no-op and will return the Result (but Ok => Ok(Some)).

Source

fn to_anyhow<T, E>( &self, result: Result<T, Self::Error<E>>, ) -> Result<T, Self::Error<Error>>
where Error: From<E>,

Convenience to convert the inner Error to anyhow::Error.

Source

fn map_err<T, E1, E2>( &self, result: Result<T, Self::Error<E1>>, map_err: impl FnOnce(E1) -> E2, ) -> Result<T, Self::Error<E2>>

Convenience to map the inner Error.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§