#Rust2019 blog post: The Last Thing Rust Needs

The only accepted form of Ok-wrapping (to my knowledge) is what was originally accepted as part of the ? + catch block RFC: RFC243 § catch expressions. Since then "catch expressions" have been renamed to "try blocks" in RFC2388.

This is a very limited form of Ok-wrapping, only the implicit return value from a try block is wrapped into the success variant of the block's return type.


While I do agree with some of the sentiment of the blog post, this example specifically I feel does reduce its impact somewhat. Especially this specific quote really annoyed me while reading it:

However, after a short period of getting used to, the (minor) pain associated with having to write an explicit Ok or return Err(...) quickly fades away.

I have been writing Rust as a hobby (and a very small bit of work) language for the last 4 years, and still every single time I have to write Ok(()) at the end of a small closure that does some IO I feel the readability pain. Maybe the pain has been numbed slightly by repeated infliction, but I really wouldn't say it's faded away.

9 Likes

That second RFC says

// Note: OK-wrapping is assumed here, but it is not the goal of this RFC
// to decide either in favor or against OK-wrapping.

which seems a bit tenuous to me, I dunno.

The second RFC was just changing the keyword without affecting semantics, the original RFC specified Ok-wrapping, but also #41414 is linked from the tracking issue as the issue that resolved whether Ok-wrapping should happen or not.

5 Likes

I did tried to implement OK-Wrapping in Rust library during the development of CeX-0.3.0-alpha, which is a library to support throws in function signatures.

I think it is feasible to implement OK-Wrapping in library. But I did not finish my poc, which works for simple cases but need improved to be sound.

I stopped because I doubt if it is necessary. As I understand, OK-Wrapping will break some basic Rust syntax rules, which will lead to confusion.

  1. The mismatch between returned value and the function return type in signature.

  2. The mismatched type between if/else branches

  3. The mismatched type between arms of one match.

2 Likes

Is there a summary somewhere on what the state of try ... catch and Ok-wrapping is? I just read through some of @Nemo157 links (and then some), and I can’t make heads or tails of the situation. Is the RFC that introduced catch expressions still accurate?

I really do not want this thread to devolve into a debate about Ok-wrapping. I’m interested in understanding what the current state is and, if possible, where the most productive place to leave feedback would be.

4 Likes

Exactly, for this reason I was trying to search for the latest Ok-wrapping RFC to hopefully form a more informed opinion on it, but unfortunately the only one I was able to find was the one already mentioned here. Which by the way has been closed, so I’m not sure if it’s in sync with the currently implemented or expected behavior.

As far as I can tell #49371 is the last relevant PR and makes try-blocks work exactly how catch-expressions were specified to work in the RFC (modulo being extended to support the Try trait rather than being hard-coded to Result). The tracking issue would definitely be the place to discuss this further, but as @scottmcm “recently” said this would be re-re-raising an issue that has been debated many times before.


Which by the way has been closed

What do you mean by that? The RFC was accepted years ago and the tracking issue is still open waiting on stabilisation.

1 Like

That RFC explicitly did not stabilize catch expressions. I remember that very clearly because it was a compromise made in order to stabilize ?, which had much more consensus. I also remember this because I had concerns about catch at the time but decided not to voice them because they weren't on the table for stabilization. I don't have time right now to go back through the hundreds of comments and tell Github 10 times to really show me every hidden comment to find where this was said, but it should be there.

1 Like

RFCs are not usually how we stabilize things, and I think you’re conflating two different events:

  • We merged an RFC which contained both ? and catch expressions.
  • We stabilized the ? expression on Result, but nothing else from the RFC through an issue FCP.

Further changes have been made since then, but we accepted an RFC to have catch expressions on nightly under a feature flag way back in 2015.

The original RFC specified that the final expression of a catch block is expected to unify with the “ok” type of the result (i.e. it is “ok wrapped”).

(We’ve since also stabilized ? on Option, but I believe still nothing else from that RFC.)

Thanks for the clarification, although I think I’m just more confused than I was before. What was the concrete distinction being made by having that RFC be accepted for ? without stabilizing catch expressions? My understanding, I think, was that there would be an opportunity to further discuss whether catch should be stabilized. Did I just miss that discussion? If so, it happens. It’s hard to keep up! Or is the discussion still going on? And what exactly is it that is going to be stabilized?

There are two major "consensus seeking" milestones in the lifetime of a feature: when the RFC is accepted (allowing it to be implemented on nightly), and when the stabilization FCP is completed (allowing it to be stabilized). Catch expressions passed the first milestone in 2015 (as part of an RFC that also includes the ? expression and other things), but have still not passed the second milestone.

Your posts are conflating "RFC merged" with "feature stabilized." No one has ever even proposed to stabilize catch expressions, AFAIK.

1 Like

OK, I went back to the thread and now see why I was confused. Everyone on that thread is using “stabilized” like how I’m using it here. e.g., https://github.com/rust-lang/rust/issues/31436#issuecomment-251681299 — I think probably our conception of the RFC/stabilization process has crystallized a bit more since then, but my head was still stuck in 2016.

1 Like

Sorry, I meant that the PR was closed – and I confused it with the RFC being closed. (I though the PR was supposed to be merged – it doesn’t matter much, though.)

To add a bit more context, the entire concept of a non-Ok-wrapping catch block arose during implementation. It worked that way on nightly for a while, until someone raised the question of “why is the implementation different from the RFC” and that’s when that issue was filed.

Meanwhile, there was at least one attempt to solve the Ok(()) paint point via coercion rather than wrapping, as a thread on i.r-l.o. Niko summarized some of the issues with that approach here, and the implementation was changed to match the RFC.

2 Likes

Isn't Ok-wrapping and all that simply doable with macros? (e.g. crates.io: Rust Package Registry)

EDIT: More generally, I agree with @Cazadorro's

1 Like

Should the fact that something can be done using macros disqualify it as a potential language feature?

I’m never particularly thrilled when I need to use macros that contain blocks of code. It introduces potential tooling and UX issues and will never be as good an experience as a first-class feature.

That said, I’m not making a judgement about Ok-wrapping either way, other than to say that I would prefer a first-class language feature or nothing at all. A standard macro that would see widespread use and encapsulate large blocks of code would introduce more headaches than a simple language feature, IMO.

7 Likes

Everything comes with trade-offs. I did read your statement, and I still want more heuristics to help decide these things. Concretely, give me a bunch of questions I should ask myself before checking a pFCP-merge box to be consistent with the "philosophy of Rust as a whole".

But when does an idiom become so common as to be worth codifying with a language feature? It's like the old complaint of "Design Patterns are just missing language features".

What is it about while let Some(x) = y.next() that it was worth adding for x in y? What is it about loop { if !x { break } that it was worth adding while x {? What is it about match x { true => that made it worth adding if x?

And in the other direction, what is it about loop (result, count) = (1, x) { that has it in p-FCP close? What is it about _ => {} that it's not omit-able like else{} is?

Could one not also say that the (minor) pain associate with having to write true => or else{} quickly fades away?

6 Likes

An argument that I feel has strongly resonated in a few of these blog posts, iincluding @H2CO3's (without diving in to whether we should are shouldn't support this or that individual new feature) is that:

A lot of new features have been added (to nightly and stable), a decent portion of which have been stabilized, and that the ecosystem hasn't had time to experiment with how those features change the experience of writing Rust code, esp. with regards to Rust 2018.

Generally, I think in the vein of the OP (ie. the blog post) there is a strong argument in increasing the bar for how much scrutiny (and how high a complexity/benefit bar) an RFC should be subjected for say the next 6 months or a year, in that our opinions on many RFCs may change when we are writing code quite a bit differently (certain things like async, we've already bitten the complexity bullet on at least wrt. reserving the keywords).

Similarly there are a fair number of features that folks have proposed that are still on nightly, and a lot of them I personally believe might substantially change how rust code is written on an ecosystem wide scale (such as try { ... } blocks, irregardles of wrapping or not) but also async, generators, etc, etc, etc, etc.

Stabilizing (or alternatively, would we "decide" we didn't want to stabilize a feature?) these nightly features falls in to the narrative of handling the Rust comunity's technical debt certainly from T-compiler perspective, (but maybe sometimes from a T-lang perspective?).

Aside: Some issues are unstable for reasons such as blocking on chalk or another compiler improvement, but AFAIK try blocks aren't one of those?

As part of the ongoing discussions going on around Rust 2018 Edition and the Roadmap for Rust in 2019, some sort of snapshot view of ongoing effort and implemented on nightly, vs. unimplemented but in an accepted RFC would definitely be valuable towards helping bring the wider rust community onto a similar page.

^ and more specifically, what is the blocker on things (contributor time, vs. X/Y/Z refactor).

I suspect a link might already exist documenting that, but I'm not aware of it.



That said, a lot of folks have ideas that they'd like to try, and to suggest that we "slow down" or "increase the burden" of RFCs might feel unfair to all the new rustacean's that have been invited in as part of the goal of releasing an edition---

On this path, I wonder if a stronger encouragement towards eRFCs (experimental) and other sorts of "lower commitment" mechanisms might be a good compromise between a fairly obvious need to deal with accumulated debt and the desire to support, encourage, and harness the drive of contributors/users who have a particular issue they are passionate about.

2 Likes

If something can be done using macros there is no reason to rush implementing it into the language; the idea is to keep those features as macros until they become so pervasive that adding them to the language is a low-risk and highly rewarding operation (e.g. try! becoming the ? postfix operator)

For instance, back on the try blocks topic, stabilization of the feature should not be rushed no matter how pressing it may be, since we have macros to satisfy most potential needs. This last point is one that may be overlooked, that's all I am saying.

4 Likes

Though it should be noted that this specific feature does have specific benefits to being a language built-in (around type inference) than a macro version using closures, as did ? over the original try! macro.

I agree with the general sentiment, but without specific particulars it’s such an opinionated and vague guideline that it’s not really actionable.

Maybe as a community we should be a bit less afraid of macros. Metadata so that tools can work better with them? But we can’t lose track of “stability without stagnation”.

1 Like