Pre-RFC: attempt! macro


#1

I often find myself tired of typing

let something = match something {
    Ok(ok) => ok,
    Err(_) => {
        eprintln!("Oh no, something failed D:");
        eprintln!("Try taking a glass of water and trying again!");
        return;
    }
};

.

I love match in general, but for errors it’s just so much boilerplate for so little. I often just make an attempt macro:

macro_rules! attempt_or {
    ($result:expr, $fail:block) => {
        match $result {
            Ok(ok) => ok,
            Err(_) => {
                $fail
            }
        }
    }
}
let something = attempt_or!(something, {
    eprintln!("Oh no, something failed D:");
    eprintln!("Try taking a glass of water and trying again!");
    return;
});

This works, but it’s boring to rewrite that macro for every single project. Also, it doesn’t give us access to the err variable.

It would be absolutely amazing to have a macro like this (but better) built in to the STD, for simply extracting a value while still giving complete control over the error.


Thoughts?


EDIT: Another problem with this macro is that you have to return or break or similar, otherwise it complains about incompatible match types.


#2

This macro inhabits the same design space as the guard let ... else pattern, but is less expressive (as it limits itself to Tryable things?). Check out this RFC: https://github.com/rust-lang/rfcs/pull/1303 It was postponed, but I think that many people would like to see that pattern in Rust, so the idea isn’t dead.


#3

If your main goal is to describe the error, then I suggest

let something = something.map_err(|_| Error("oh noo"))?;

With error_chain or custom error type you can make it ergonomic to add context.


#4

I’d very much prefer to customize it more than that…


#5

If we could figure out a clean way to support an outer return this pattern would fall out naturally.

let something = something.unwrap_or_else(|| {
    eprintln!("Oh no, something failed D:");
    eprintln!("Try taking a glass of water and trying again!");
    
    // return from captured fn (other syntax ideas were proposed in the thread)
    // I like this one the best as it resembles break labels
    return 'fn;
});

I opened an internal thread on that here: pre-RFC: Allow return, continue, and break to affect the captured environment


#6

I assume that would only apply to FnMut, right?