How to early return Ok from try block to the end of try block instead of fn block?

#![feature(try_blocks)]

use eyre::{Ok, Result, eyre};

fn main() {
    let res: Result<()> = try {
        if true {
            // Jump to the end of try block with Ok value assigned to res
            return Ok(());
        }
        Err(eyre!("Unreachable"))?;
    };
    res.unwrap();
}
error[E0308]: mismatched types
 --> src/main.rs:8:20
  |
5 | fn main() {
  |          - expected `()` because of default return type
...
8 |             return Ok(());
  |                    ^^^^^^ expected `()`, found `Result<(), Report>`
  |
  = note: expected unit type `()`
                  found enum `Result<(), ErrReport>`
help: consider using `Result::expect` to unwrap the `Result<(), ErrReport>` value, panicking if the value is a `Result::Err`
  |
8 |             return Ok(()).expect("REASON");
  |                          +++++++++++++++++

I don't think it was possible. IIRC, try blocks only interact with the question mark operator, it has not effect on return.

instead of a try block, I usually abuse Result::and_then() (or Option::and_then()) to create a inner scope for to use the question mark operator:

// sometimes, type annotation is needed
// alternative is to use eyre::Ok
let res = Ok::<(), eyre::Report>(()).and_then(|_| {
    if true {
        return Ok(());
    }
    Err(eyre!("unreachable"))?;
});
res.unwrap();
1 Like

Thanks for the closure trick!

In that case, I'd think it's simpler to do

fn main() {
    let f = || {
        if true {
            return Ok(());
        }
        Err(eyre!("Unreachable"))
    };
    f().unwrap();
}

With Ok? Not possible.

With Err there's yeet_expr - The Rust Unstable Book though.

2 Likes

Thank you!

Yeah, before I checked out yeet, and I thought yeet could do

do yeet Ok(...)

and found it's designed for Err.

(On mobile, excuse any brevity.)

After some playing around, I wondered - why not labeled try? And the answer is Ok wrapping. It's unclear if a break to a labeled try block should do so or not.

In the middle of the massive tracking issue:

So you can do it with two blocks in a couple ways.

2 Likes

it's a personal taste, I guess. I find myself use and_then() a lot when I want to access a chain of nested optional fields, similar to the ?. operator (I don't remember what it is called) in typescript:

let mayby_xyz = Some(&foo).and_then(|foo| {
    foo
        .bar.as_ref()?
        .baz.as_ref()?
        .qux.as_ref()?
        .xyz.as_ref()
});

of course, it is the same to write as an IIFE:

let maybe_xyz = (|| {
    foo
        .bar.as_ref()?
        .baz.as_ref()?
        .qux.as_ref()?
        .xyz.as_ref()
})();

I just personally prefer and_then(), for no particular reason.

granted, this pattern is not very common in hand written code, but may be used a lot in some machine generated data structures from some DSL model. those as_ref()s can be very annoying, but this is rust, I gotta unwrap my head around it. :wink:

1 Like

Awesome! That label-jump trick is what I'm looking for!

I indeed tried break by myself, but got an error for it break outside of a loop or labeled block.
I omitted labeled block is a thing!