Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the recommended way to produce side effects in control flow using Result?

Result::and_then() works great for composing control flow.

fn some_fn() -> Result<String, Error> {
    Ok("Yay".to_string())
}
some_fn()
    .and_then(|value| some_other_fn())
    .and_then(|some_other_value| /* ... */)

Sometimes we want to create a side effect and still propagate the emitted value. Let's say we want to print the value the moment we receive it:

some_fn()
    .and_then(|value| {
        println!("{}", &value);
        some_other_fn()
    })
    .and_then(|some_other_value| /* ... */)

Is there a better way to do this? Something like Reactive Extensions' tap() operator would be great.

like image 580
Pascal Precht Avatar asked Oct 15 '25 07:10

Pascal Precht


1 Answers

map and and_then are appropriate for when you want to transform the value. Using match or if let is appropriate for side effects:

let r = some_fn();

if let Ok(v) = &r {
    println!("{}", v);
}

r.and_then(|_| some_other_fn())

See also:

  • Why does Rust need the `if let` syntax?

Assuming that you only care about side effects when the Result is Ok...

You could also create an extension trait to add the desired method to Result. I'd advocate for calling it inspect as that's the name of the parallel method on Iterator.

trait InspectExt<T> {
    fn inspect<F>(self, f: F) -> Self
    where
        F: FnOnce(&T);
}

impl<T, E> InspectExt<T> for Result<T, E> {
    fn inspect<F>(self, f: F) -> Self
    where
        F: FnOnce(&T),
    {
        if let Ok(v) = &self {
            f(v)
        }
        self
    }
}
some_fn()
    .inspect(|v| println!("{}", v))
    .and_then(|_| some_fn())

See also:

  • Is there a way other than traits to add methods to a type I don't own?
like image 55
Shepmaster Avatar answered Oct 17 '25 20:10

Shepmaster



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!