Skip to content
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

RFC: throw expressions #2426

Closed
wants to merge 9 commits into from
Closed

Conversation

Centril
Copy link
Contributor

@Centril Centril commented Apr 30, 2018

🖼️ Rendered

📝 Summary

Introduce diverging throw expr expressions, typed at !, which either break to the closest try { .. } if there is one, or if not, return from the fn or closure. The expression form throw expr is supported on edition 2018 onwards. This also means that throw is reserved as a keyword.

A minimal example:

if condition {
    throw Foo;
}

💖 Thanks

To @scottmcm for reviewing.

@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Apr 30, 2018
text/0000-throw-expr.md Outdated Show resolved Hide resolved
@Ixrec
Copy link
Contributor

Ixrec commented May 1, 2018

I guess I'll be the one to make the highly subjective but critical point against this, since I think we need to start gathering opinions on it ASAP if it's going to get addressed properly:

I'm still not convinced that try {} blocks and throw expressions pull their weight as language features.

In fact, the motivation section for this RFC made me more skeptical that throw is worthwhile, since in all the examples it shows, we only appear to be saving a half-dozen or a dozen characters at most. With ? we were saving not only a whole match block, but it was very common to see nesting of those match blocks (or the corresponding try!()s). I'm not aware of expressions like return Err(error) ending up "nested" in a way that helps motivate throw.

I know try {} blocks aren't part of this RFC, but I think throw only stands a chance of pulling its weight if it comes with the convenience of targeting the enclosing try {} block or the whole function if there is no enclosing try {} block, and if try {} blocks are used so frequently that this is a big deal. And I don't think it's been established that there's a clear consensus ontry {} blocks themselves being worthwhile (despite their presence in the original ? RFC).

Also, the prior art section feels highly misleading, since this feature has nothing to do with implicitly-propagated exceptions, and many of the languages listed are using throw specifically for exceptions, not early-but-otherwise-ordinary returns. While I'd prefer to use a keyword without the exception baggage if we do this at all, I don't want to start that bikeshed yet because it's far more important to come to a consensus on whether any special sugar for "non-exception throws" pulls its weight. And that's only going to happen if we get a bunch of people here stating their personal opinion not on the optimal keyword, but of the value of having try {} and throw expr; features at all, regardless of their final syntax.

@nikomatsakis
Copy link
Contributor

I approve.


In response to @Ixrec's concern:

I'm still not convinced that try {} blocks and throw expressions pull their weight as language features.

I would say two things:

First, I don't consider this feature to only make sense if we have try blocks. They seem orthogonal to me. As discussed in the RFC, there is a definite need to be able to unconditionally produce an error, even today -- and getting the precise formula correct is tedious. This is why error-chain and failure both include bail! macros.

Second, even if we are not sure we want something, if we think that there is a "pretty good chance" that we do, then it is worth reserving the keyword. In this case, I think the answer is definitely yes: both because there is prior art in favor and because there are a lot of folks who have spoken up in favor of throw or something similar in various threads and informal discussions.

@Centril
Copy link
Contributor Author

Centril commented May 1, 2018

@Ixrec

[..] we only appear to be saving a half-dozen or a dozen characters at most.

While a bonus of throw expr is brevity, in particular for generic cases where it substitutes
return Try::from_error(expr), this is not the main motivation. Instead, the main benefit is better highlighting of exceptional cases via distinct syntax. This improves the at-a-glance readability of code.
Also, for me, throw expr telegraphs much better than return Err(expr) the semantic intent of the code and the author who wrote it.

[..] targeting the enclosing try {} block or the whole function if there is no enclosing try {} block, [..]

Just to clarify, this is the semantics of throw expr in this RFC. :)

[..] and if try {} blocks are used so frequently that this is a big deal. And I don't think it's been established that there's a clear consensus ontry {} blocks themselves being worthwhile (despite their presence in the original ? RFC).

As @nikomatsakis puts it, throw is beneficial even without try { .. }, for reasons I mention above. However, I do think that try { .. } and throw together do enhance each other.

I would also say that the try { .. } RFC, #2388 which is in FCP should count for something. Assuming the RFC gets merged (in one day), this means that try { .. } has been confirmed as useful in two separate RFCs including the original one you reference.

Also, the prior art section feels highly misleading, since this feature has nothing to do with implicitly-propagated exceptions,

Neither does try { .. }, but two consecutive RFCs has advocated that we have manually propagated exceptions and that the benefit from familiarity outweighs the implicit / explicit distinction.
My view is that throw expr; would play the same role in both Java and Rust despite there being semantic differences.
Therefore, the prior art is, in my view, not misleading at all, assuming we remember the semantic difference between Rust and Java here. The section on drawbacks does discuss these differences.

and many of the languages listed are using throw specifically for exceptions, not early-but-otherwise-ordinary returns.

Many, but not all. Haskell and Idris use throwError and raise respectively by convention despite preserving manual propagation (via monads) and errors as values semantically.
Swift is another example of not using Java style exceptions but still having throw.

While I'd prefer to use a keyword without the exception baggage if we do this at all, I don't want to start that bikeshed yet because it's far more important to come to a consensus on whether any special sugar for "non-exception throws" pulls its weight.

I disagree with the notion that Err(x) is not an exception and thus that throw would be "non-exception throws". To me, Err denotes the exceptional case while Ok denotes the normal case.

On the choice of keyword, I think we should pick something that is consistent with try { .. } and that exceptional terminology.

@scottmcm
Copy link
Member

scottmcm commented May 1, 2018

it is worth reserving the keyword

👍

This is my strongest feeling in this area. Some sort of throw expression has been discussed for at least three years; now that we're making an epoch it seems clear we should reserve the keyword. In the absolute worst case it's trivial to un-reserve it later, as that doesn't need an epoch.

@repax
Copy link

repax commented May 1, 2018

Thanks, @Centril for this fine RFC! 🥇

On the choice of keyword, I think we should pick something that is consistent with try { .. } and that exceptional terminology.

Here's my comment: I think we should find a better keyword than throw. I do like try {...}, but throw presupposes a subsequent catch() {...} block - an unlikely route for Rust - and catch is not suitable because success values are not thrown.

I'd like to stress this point. Our experimental do catch is nothing like the catch() {...} of other languages. Our do catch is really a try {...}.

We need a keyword that clearly expresses the occurrence of a failure, and is consistent with a failed attempt, a failed try.

@Centril
Copy link
Contributor Author

Centril commented May 1, 2018

@repax

Thanks, @Centril for this fine RFC! 🥇

I do love the appreciation! 😍

I think we should find a better keyword than throw.

Ideas? 😄

Here are some synonyms of throw:

I do like try {...}, but throw presupposes a subsequent catch() {...} block

How so? I understand that you might say: - "I threw the ball and my dog caught it", but the meaning of throw Foo seems intuitive even without a catch block due to familiarity.

an unlikely route for Rust

I'm wary of making such predictions either way since it is difficult; We can give technical and non-technical arguments for why catch should or should not be added to Rust, but extending this to prediction seems fraught with perils.

We need a keyword that clearly expresses the occurrence of a failure, and is consistent with a failed attempt, a failed try.

This way of framing it seems to suggest fail as a keyword which fits naturally with try if you say "you might try, but you might fail". It is also a good candidate since the semantics should be scrutable by someone who has never seen fail expr before. But there are a few drawbacks with fail:

  1. There's a crate named fail that has reverse dependencies (the rev deps are all by one author however).
  2. It is too close to bail! (which can also be seen as a pro).

All in all, I think @nikomatsakis put it well on #2388:

Rust has a lot of concepts to learn. If we are going to succeed, it's essential that people can learn them a bit at a time, and that we not throw everything at you at once. I think we should always be on the lookout for places where we can build on intuitions from other languages; it doesn't have to be a 100% match to be useful.

This suggests to me that throw is the right choice.

@burdges
Copy link

burdges commented May 1, 2018

We do need to tell people "Rust does not have exceptions", so using throw creates confusion, while fail actually nails it rather well. I would not worry about one crate name, but maybe numerous traits have fail methods, and making it contextual sounds suboptimal.

@rpjohnst
Copy link

rpjohnst commented May 1, 2018

Back before 1.0, panic used to be called fail. It was always kind of confusing what was meant when someone said "and then function xyz failed," because they might mean it in the general "opposite of succeed" sense or in the "unwind the stack" sense.

In this case, I don't think that confusion will come up. While there are still several ways to fail, they all tend to do the same thing as fail would- return an error value. So I kind of like fail here: it fits well with try, it fits well with the try_ naming convention, it avoids throw's potential confusion with catch_panic, and if Failure is standardized it will match that as well.

I'm not really opposed to throw, though it doesn't sit as well with me as try. It implies non-local control flow a bit more strongly to me than try does, as try is more about containing control flow while throw is more about initiating it.

I also have a general sense of uneasiness about introducing too many return/break/continue-alikes, when they all feel kind of ad-hoc. I like the extension of break to loops and blocks, but throw/fail doesn't quite feel as coherent, especially in such an expression-based language. You can return and break with values, but throw/fail only covers errors- there's no way to early-out of a try block with a success value, and I'm not sure we want yet another keyword to accomplish that.

But at the same time I do think Err(x)? is kind of obnoxious, and return Err(x) doesn't do error conversion. Maybe we just need to hook up return/break to Ok-wrapping somehow? Especially considering a hypothetical future try fn syntax where early returns should do Ok-wrapping. Then fail might feel more natural.

@Centril
Copy link
Contributor Author

Centril commented May 1, 2018

@burdges

We do need to tell people "Rust does not have exceptions", so using throw creates confusion, [..]

Surely no more so than than try { .. } does?

I would not worry about one crate name, but maybe numerous traits have fail methods, and making it contextual sounds suboptimal.

Here's a fairly accurate check I think: https://sourcegraph.com/search?q=repogroup:crates+case:yes++\b((let|const|type|)\s%2Bfail\s%2B%3D|(fn|impl|mod|struct|enum|union|trait)\s%2Bfail)\b+max:400

The breakage doesn't seem too extensive.


@rpjohnst I agree with your reasoning on fail, it is a good candidate. I kinda like throw 60% and fail 40% but I could certainly be persuaded in favor of fail.

[..], it avoids throw's potential confusion with catch_panic, [..]

This one seems unlikely? I think at least any confusion here is quickly fixable in Rust's learning material.

I also have a general sense of uneasiness about introducing too many return/break/continue-alikes, when they all feels kind of ad-hoc.

throw being a bit ad-hoc for initiating exceptions may be a good thing tho?
Having a built-in DSL tailored to error handling seems warranted given how fundamental that sort of logic is :)

there's no way to early-out of a try block with a success value, and I'm not sure we want yet another keyword to accomplish that.

True; I agree this is a problem, but let's not let the perfect be the enemy of the good?
I don't know how I feel about it, but it has been suggested that return expr inside try { .. } could be the success version of throw expr; This fits with the idea that try fn is more or less just fn ... { try { .. } }.

@repax
Copy link

repax commented May 1, 2018

@Centril

an unlikely route for Rust

I'm wary of making such predictions

Why not catch? Because errors are values and we already have match, which does a marvellous job.

Why not throw? Because what you may throw you catch. If however, throw as a keyword is accepted by the Rust core team then I recommend that catch is reserved too. They go together.

I'd prefer fail though. Like throw-catch go together, try-fail go together, imo, and I think its meaning obvious at a glance.

@Centril
Copy link
Contributor Author

Centril commented May 1, 2018

@repax

Why not catch? Because errors are values and we already have match, which does a marvellous job.

Sure; but catch could be even more great; match has two problems in this context:

  1. it requires you to either:
    1. create a temporary binding and then match: let foo = try { .. }; match foo { .. }
    2. write match try { .. } { .. } which requires holding more in working memory than
      try { .. } match { .. }. Speaking of which, this expression form might be a good idea for more natural reading flow?
  2. It requires you to destructure Ok(x) => EXPR and Err(x) => EXPR while catch can let you destructure x inside Err(x) immediately. Of course, matching on the constructors of Result<T, E> also informs type inference for try { .. } so there's an upside too.

Why not throw? Because what you may throw you catch. If however, throw as a keyword is accepted by the Rust core team then I recommend that catch is reserved too. They go together.

You don't need to reserve catch to get try { .. } catch { .. } working :)
All that is needed is to check for try { .. } $ident { .. } throw a parsing error, and then introduce try { .. } catch { .. } later.

Regarding "what you may throw, you may catch", while I think that makes sense in English, would throw without catch handlers be confusing to a Java programmer introduced to Rust? I have doubts that this is true.

try-fail go together, imo, and I think its meaning obvious at a glance.

This seems true to me, I can't argue with it. :)

is deemed quite low.
Searching for `raise` with sourcegraph indicates a very
small number of uses as an identifier, but searching for `throw` gives a timeout
indicating that there are no uses.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got at least 4 results from https://sourcegraph.com/search?q=repogroup:crates+%5Cbfn%5Cs%2Bthrow%5Cb.

All of them are JS bindings (e.g. servo/servo and neon-bindings/neon), but I guess it is because of sourcegraph's selection of repositories.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange. I tried the following query before: https://sourcegraph.com/search?q=repogroup:crates+case:yes++\b((let|const|type|)\s%2Bthrow\s%2B%3D|(fn|impl|mod|struct|enum|union|trait)\s%2Bthrow)\b+max:400 and it timed out, but now it shows the results you reference.

Anyways, the breakage slightly is less extensive than for raise and fail; but all keywords in this space break minimally.

I'll update the RFC with this new info :)

@Centril
Copy link
Contributor Author

Centril commented May 1, 2018

I've now added some of the recent discussion to the RFC's text.

the perfect be the enemy of the good.

A strategy for introducing early-return on success with Ok-wrapping **could**
be to introduce `try fn` and / or to let `return` perform an early return
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, go the other way around- let people break out of try blocks. On its own, this is probably a bad idea, as try blocks are not loops, but it's possible 'label: try { .. break 'label value; .. } would be sufficient. For that matter, maybe any return or break that leaves the try block should be Ok-wrapped? For example:

fn f() -> Result<T, U> {
    try {
        let t: T = get_a_t()?;
        return t;
    }
}

Maybe that's even worse.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern wrt. re-purposing break is this:

try {
    // ..
    loop {
        // Are we breaking the loop or the try?
        // Perhaps it is obvious to say "the loop", but is it really?
        break 3;
    }
    // ..
}

Maybe that's even worse.

Probably, yeah.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding break with label:

'label: try {
    ..
    break 'label value;
    ..
}

That seems workable, but it is not particularly ergonomic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I'm a fan of having label-break-value, I see it mostly as a way for macros to provide customized flow control constructs. I'd rather it typically be internal to libraries more than something that shows up in syntax that would be used broadly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another, possibly bad, idea to facilitate for macros could be to introduce the special label 'try which always refers to the closest try { .. } block.

That way, you could just write:

try {
    ok!(value);
}

where ok!(value) expands to break 'try Try::from_ok(value).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Centril I think my rigorous reason is I don't like introducing new names "out of thin air." Every other label has its name chosen by the source code- I would be much less bothered by some kind of 'my_label: fn f() { ... } or 'my_label: try { ... } than I am by introducing 'fn or 'try as "special" names.

A similar-but-different situation came up in #2115, where people felt wary about allowing the source code to introduce names without some primary declaration point. But in that case, the source code is still the thing determining the name- you write the same name in two places and they mean the same thing. Labels inherently have a "declaration" point, but the same reasoning applies- you write the name down in both places, as a label.

(@squishy-clouds 'static is not a label at all- it's a lifetime, and further kind of a "monoidal identity element" like 0 or 1 or [], so it gets a bit of a pass here.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rpjohnst I don't think they would be so "out of thin air";

If the system of having special labels is applied out consistently per control flow block-form you could have:

  • 'fn
  • 'loop
    • 'while
    • 'for
    • maybe just merge these as one loop?
  • 'if
  • 'match
  • 'try
  • 'async
  • 'const (maybe not?)

The goal of these are not to be used directly in people's code, but rather hidden away in macros so that you can invent new and interesting DSLs. The problem with having to explicitly declare the labels is that you have to pass them into the macros.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any actual use cases for a generalized "magic labels" feature like this, even in DSLs? I've never heard anyone suggest any of these except as an implementation detail of a throw/fail macro.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One use-case might be that if we had a 'try built-in label that can be targeted by ?, I think all of the other exception like syntax can be implemented by the eco-system (or even std if deemed worthy) as macros. That would allow for:

  • People to use break-with-value and ? with these blocks without auto-wrapping or any other additional functionality, basically keeping it as close to current control flow as possible.
  • Experimentation with more exception like syntax on crates.io. Before things would settle that would also provide an explicit opt-in to specific semantics. Once things have settled, the functionality can simply be added to std. Since they would still be macros, there's still an opt-in to additional semantics like auto-converting a final result. Adjusting pieces of std also seems easier and more future proof than changing the language itself.

I'm wondering if it would make sense to put together a proposal for this path for the new exception like syntax extensions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any actual use cases for a generalized "magic labels" feature like this, even in DSLs?

Not very concretely at least; Just hypothesizing ;)

@kennytm
Copy link
Member

kennytm commented May 2, 2018

Stemming from #2426 (review)... Would this be (eventually) allowed?

let result = 'a: try {
    try {
        throw 'a e;
    }
};

@Centril
Copy link
Contributor Author

Centril commented May 2, 2018

@kennytm

Stemming from #2426 (review)... Would this be (eventually) allowed?

I suppose it could. It is consistent with break 'a e; I don't have any arguments against it other than:

  • "how often do you want / need to do this?"

Another possibility is discussed here: https://github.com/Centril/rfcs/blob/rfc/throw-expr/text/0000-throw-expr.md#paper-exceptional-syntax

@joshtriplett
Copy link
Member

I'm not objecting to the idea of having an expression that serves this function (though personally I think return Err(e) seems quite clear), but the more I think about it and the more discussions I've seen about error handling in Rust, the less inclined I find myself to choose keywords and constructs evocative of exceptions.

@Centril
Copy link
Contributor Author

Centril commented May 2, 2018

@joshtriplett How come? And are you saying that we should choose fail instead?

I do think that RFC #243 using exceptional terminology should be seen as precedent and therefore the case against exceptional terminology is less convincing.

However; my view is that either throw or fail would better than neither throw or fail (to not have the feature at all), and so if the lang team finds fail better, I'll happily switch to that.

@petrochenkov, @wesleywiser, @Flaise, @kjeremy, @gilescope @phaylon @alexander-irbis @0x7CFE:
Could you please note your specific concerns so that I can address them?

@phaylon
Copy link

phaylon commented May 2, 2018

I agree with the concerns already stated, and my concerns from the try discussion carry over to here.

I'd summarize it as: I'm generally against using exception syntax in a non-exception language, because, mainly:

  • The semantics of Rust error handling and exception based error handling are at odds, and so are intuitions based on them.
  • It special cases errors more than I'd like, since I believe errors should always clearly be normal values.
  • Every new control flow construct increases the language surface in a critical place. I don't believe the advantages here (not writing Err) are worth the complexity increase.
  • With the auto-conversion included, it's another place where type conversion happens without being explicitly requested.

These are largely philosophical/foundational, so I'm not sure there is much to address. I believe I will remain firmly in the -1 group.

@Centril
Copy link
Contributor Author

Centril commented May 2, 2018

@phaylon Would you be more comfortable with fail expr perhaps?

With the auto-conversion included, it's another place where type conversion happens without being explicitly requested.

It is not included in the current proposal (and doesn't have to be).

I don't believe the advantages here (not writing Err) are worth the complexity increase.

Writing return Err(expr) is not equivalent to throw expr in try { .. } so this is not the only advantage. Result is also not the only type implementing Try. In a generic setting, you'd have to write: return Try::from_error(expr) which I find much less readable.

@ibkevg
Copy link

ibkevg commented May 8, 2018

@ssokolow Thanks for giving me the benefit of the doubt, but it was a typo. I need to issue an RFWIMNWIS, Request for What I Meant, Not What I Said! :D

@mikeyhew
Copy link

This is a very well-writren and thoroughly researched RFC. @Centril I like how neutral you remain when discussing the options, and although you argue for throw as your favourite choice, you make it clear that it's just your opinion and still point out the advantages of the other choices.

Just wanted to add a couple thoughts:

  • I like fail, and the reason fail <expr> didn't sound weird to me is because I insert the word "with" in between, e.g. "fail with this error". break <expr> works similarly.
  • This has been mentioned many times, but it would be nice for consistency of we had an Ok-wrapping version of throw/raise/fail. Unfortunately the potential keywords for that are not as good — pass? succeed? ok? return? pure? — but it would be a shame if that were the only reason keeping us from having it.

@Centril
Copy link
Contributor Author

Centril commented May 14, 2018

Since the only urgent question of this RFC is the keyword reservation of throw and fail,
I have written a separate proposal in RFC #2441, to do just that and nothing more,
so that this RFC does not have the same urgency.

@scottmcm
Copy link
Member

@rfcbot fcp postpone

"The Rust team is laser focused on the 2018 Edition right now", so with #2441 covering the urgent-for-the-edition part, I propose we set aside any further discussion until after the current roadmap.

@rfcbot
Copy link
Collaborator

rfcbot commented May 20, 2018

Team member @scottmcm has proposed to postpone this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-postpone This RFC is in PFCP or FCP with a disposition to postpone it. labels May 20, 2018
@rfcbot
Copy link
Collaborator

rfcbot commented May 23, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot added final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. and removed proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. labels May 23, 2018
@rust-lang rust-lang deleted a comment from mrcnski May 23, 2018
@rust-lang rust-lang deleted a comment from mark-i-m May 23, 2018
@Manishearth
Copy link
Member

Manishearth commented May 23, 2018

Moderation note:

@m-cat such comments are very clearly against our code of conduct. Further such behavior will result in a ban from the project.

Feel free to contact us at rust-mods@rust-lang.org if you have questions.

@ghost

This comment has been minimized.

@mbrubeck
Copy link
Contributor

[Comments/questions about moderation should go to the moderation team, please.]

@mrcnski

This comment has been minimized.

@Manishearth
Copy link
Member

Feel free to contact us at rust-mods@rust-lang.org if you have questions.

This is not the place to discuss this.

@rust-lang rust-lang deleted a comment May 23, 2018
@Pzixel

This comment has been minimized.

@mbrubeck
Copy link
Contributor

Moderation note: There are multiple discussions happening in the forum about the RFC process. Please keep process discussion in the forums and out of individual RFC PRs.

@aturon
Copy link
Member

aturon commented May 23, 2018

I'm going to go ahead and close out this RFC (which is in FCP for postponement), as I think it's quite clear we won't be pushing on this in the near future, and the thread is continuing to veer well off topic.

@aturon aturon closed this May 23, 2018
@rust-lang rust-lang locked as too heated and limited conversation to collaborators May 23, 2018
@rust-lang rust-lang deleted a comment May 23, 2018
@Centril Centril added postponed RFCs that have been postponed and may be revisited at a later time. and removed disposition-postpone This RFC is in PFCP or FCP with a disposition to postpone it. labels May 23, 2018
@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this RFC. and removed final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. labels Jun 2, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
finished-final-comment-period The final comment period is finished for this RFC. postponed RFCs that have been postponed and may be revisited at a later time. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.