Making a function that converts different kinds of Results

I'm trying to make a function that takes a Result and a bool and returns
> when the Result is Ok(value) return Ok(value)
> when the Result is Err(_) and the bool is true return Ok(None)
> when the Result is Err(error) and the bool is false return Err(error.context("...")) (.context() is from anyhow).

Unfortunately I'm getting a bit tangled up in Error and Result types.

use anyhow::{anyhow, Result};
use std::fs::File;

fn convert<T, E: std::error::Error + anyhow::Context<T, E>>(
    result: std::result::Result<T, E>,
    should_retry: bool,
) -> Result<Option<T>> {
    Ok(match result {
        Ok(v) => Some(v),
        Err(err) => match should_retry {
            false => { err.context("Retries exceeded")?; },
            true => None,
        },
    })
}

fn always_fails() -> Result<()> {
    Err(anyhow!("something happened"))
}

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let result = File::open("doesnt exist");
    let file = match convert(result, false)? {
        Some(f) => f,
        None => { return Ok(()); },
    };
    
    let result = always_fails();
    match convert(result, false)? {
        Some(_) => {},
        None => {},
    };
    
    Ok(())
}

Playground

There are two context methods in anyhow. One is an inherent method on anyhow::Error and the other is on an extension trait Context which is implemented for Result[1]

I think you're confusing these two methods. If you instead just specify that E can be converted into an anyhow::Error and call the inherent method, things work with minimal changes.

You do need to actually specify return in that branch though, because it won't be an Ok, which you're wrapping the whole outer match with.

fn convert<
    T,
    // anyhow::Error has a From impl for error types that implement all the right traits. Using that saves us from having to specify all of those bounds here
    E: Into<anyhow::Error>,
>(
    result: std::result::Result<T, E>,
    should_retry: bool,
) -> Result<Option<T>> {
    Ok(match result {
        Ok(v) => Some(v),
        Err(err) => match should_retry {
            false => {
                return Err(err.into().context("Retries exceeded"));
            }
            true => None,
        },
    })
}

Playground


  1. and Option, apparently? ↩ī¸Ž

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.