Right now Result has methods that allow you to do something with the value if it's an error or if it's ok, but there's nothing that allows working on the result regardless of whether it's ok or err while passing through the error:
e.g. I want to be able to do:
let result = foo();
something();
result
As something like:
foo().then_inspect(|_| something())?;
This comes up in terminal applications where you need to restore the terminal state regardless of whether an error occurred, but are otherwise fine with letting the error be handled by the normal mechanism:
fn main() -> color_eyre::Result<()> {
color_eyer::install()?;
let terminal = init_terminal()?;
let result = run_app();
restore_terminal()?; // perhaps ignore this error...
result
}
There are many ways to write this, but none of them seem particularly neat, and this feels like a missing set of methods that are not and_xxx / or_xxx, but then_xxx
Yeah, tap makes sense if this pattern was used a lot in an application, but it seems a bit heavy to import an entire library for a single line of code like this. I mostly like the idea of having a built-in anyway path because there's aready an ok/err path. This seems like an obvious addition (even if it's an obviously wrong one ).
The thing is: this pattern of data/control flow is equally applicable for all types. (And I've written it, using plain syntax, for lots of types.) Therefore, I don't think it makes sense to propose a means to execute it for Results only. My mention of tap was not meant to suggest that you just use that library, but that the idea can and should be more general, if it's going to make it into the standard library.
(Though personally, I'd rather see some new syntax invented — if it's good — than pile more things into the method namespace.)
Heavy from the library developer's perspective. When writing example code for rustdoc / example binaries, I face the conundrum of either importing and explaining another library, writing less concise code, or only using things which are provided in the standard lib / fairly common libs. The heaviness of tap in this context is that it's not something that everyone would be aware of and so the effort to explain it would outweigh the benefit of losing 2 lines of code.
For examples, you should write the most “boring” code you possibly can. As I see it, the goal of example code is that the reader can easily take part or all of it and adapt it to their actual problem, and for that purpose, it's important that all the plumbing is visible — that the code is in a format that's easy to refactor, not cleverly compressed. So, for this application, I'd agree that you shouldn't introduce any dependencies if feasible, but I'd go further and say you also shouldn't use things like then_inspect() if it existed — those make it harder to tweak the code to do something else (especially things that interact with the enclosing function, like introducing an ? or await). A little verbosity is better than the alternative, here.
Also consider whether your library API can be improved. In the longer example in your original post, perhaps restore_terminal should be called on Drop instead of explicitly? Errors can't be reported then, of course, but as you noted, it likely makes sense to ignore (or at least, only log) that error rather than changing control flow in response to it.