Skip to content

The try! macro doesn't live up to its prominent position #12676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jfager opened this issue Mar 4, 2014 · 5 comments
Closed

The try! macro doesn't live up to its prominent position #12676

jfager opened this issue Mar 4, 2014 · 5 comments

Comments

@jfager
Copy link
Contributor

jfager commented Mar 4, 2014

I've been asked to make my general concerns about try! more clear than what #12130 specifically focused on, so I'm going to close that issue and try to do a better job here.

'try' is a short, familiar, and suggestive name. Such names are relatively rare and valuable, and would ideally be reserved for features that reach a high bar of utility and ergonomics. But the try! macro is, by design, a limited solution to the problem it's put up against, and its limitations are really easy to hit:

  • You can't use try! from main or the proc arg to spawn, even though you might reasonably expect that 'passing an error up the stack' when there is no 'up the stack' would just fail.
  • You can't use try! for 2 different Result error types in the same fn; you have to switch to another strategy for one or split them out into separate fns.
  • try! for multiple calls is still verbose and repetitive.

These limitations of course result from its simple implementation using return from a macro, and when they are brought up, solutions are easy - use unwrap, write your own macro, just drop the result, fall back to a match expression - and the point is made that try! was never intended to be a general solution.

But try! has a big footprint in the ecosystem right now. It has that valuable name. It (as if_ok!) was the only error-handling technique directly illustrated in the "Handling I/O errors" announcement of IoResult and the removal of conditions. It's the only IO error handling macro that ships in std. It shows up frequently in example code. Regardless of the original intent, it's what people reach for first when trying to figure out how to do IO.

The thing that people reach for first shouldn't have trivial headaches associated with it. The thing that shows up frequently in examples shouldn't instantly blow up when transplanted into users' first programs. Something that ships with std shouldn't require people to throw together a bunch of one-off minor variations of it in their own projects.

Or, conversely, something that has these limitations shouldn't be so visible in the ecosystem.

I don't know what the right alternative is, though I think/hope the general shape of something better would be:

  • Wraps a whole block rather just than a statement, to reduce the repetitive verbosity.
  • Is an expression, so that it can be moved around/copied from examples/used in more contexts without blowing up.
  • Keeps a short, suggestive name.
  • Keeps the must-handle warning or a moral equivalent.

I don't think wanting this basic shape amounts to wanting a silver bullet, or to trying to sweep anything under the rug, but if it's just not possible or otherwise not a good idea, I'd be interested to hear why.

The first alternative that pops to mind is a variation of Haskell's do notation, which other people have discussed elsewhere in more/better detail than I could. There's also the match! proposal on Reddit right now that may be a candidate. I'm less interested in the exact solution than in convincing the community it's a problem that should be addressed, b/c I'm sure someone else can come up with a better answer than I could.

@huonw
Copy link
Member

huonw commented Mar 4, 2014

FWIW, the error handling section of the std::io docs doesn't mention try! at all (in fact, it barely mentions anything; I think improving that will be extremely helpful).

You can't use try! for 2 different Result types in the same fn; you have to switch to another strategy for one or split them out into separate fns.

For clarity, what do you mean by this? The following works

fn f() -> Result<~str, Error> { ... }
fn g() -> Result<int, Error> { ... }

fn h() -> Result<f64, Error> {
    try!(f());
    try!(g());
    // ...
    Ok(3.14)
}

i.e. the result type differing but the error param matching. (If the error param differs then there always needs to be some custom logic for converting them to the same type, and no generic macro can perform that.)

@jfager
Copy link
Contributor Author

jfager commented Mar 4, 2014

It's not mentioned in the docs, but as you say, not much is on the topic, and when people ask "do I really have to use a match expr to handle IO errors?", they're either going to find try! from examples on github/bitbucket/reddit, or pick it up from the mailing list or irc, like basically everything else in Rust right now.

You're correct, I was mistaken on the return type point, based on having read someone saying they hit issues with that rather than having tried it myself. Apologies, I've updated the list accordingly. That particular aspect is definitely not as big of an issue with that clarification.

@brson
Copy link
Contributor

brson commented Mar 8, 2014

We do need to keep trying to improve our error handling strategy. try! was a big improvement for certain scenarios but clearly there is more to the error handling story to be discovered.

Making try! usable with Option would also be nice.

@tupshin
Copy link

tupshin commented Nov 28, 2014

FWIW, I have the very specific objective of being able to use a builder pattern that chains a number of methods that each return Result<MyType,Error>, and be able to use that builder pattern from "scripts" that only have a main method. I don't know of any alternative to try that allows you to chain together functions like that that return Results without repetitively unwrapping, etc.

@steveklabnik
Copy link
Member

This issue is a little long in the tooth, and some of it has been addressed. Additionally, it would need an RFC today. (and the ? RFC is related) So I'm giving it a close. Thanks!

bors added a commit to rust-lang-ci/rust that referenced this issue Jul 25, 2022
…ait-impl, r=jonas-schievink

fix: Extract function from trait impl

This change fixes rust-lang#10036, "Extract to function assist implements nonexistent
trait methods".

When we detect that the extraction is coming from within a trait impl, and that
a `self` param will be necessary, we adjust which `SyntaxNode` to `insert_after`,
and create a new empty `impl` block for the newly extracted function.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants