Change:
Steps: [x] Merge the implementation () [ ] Stabilization PR [ ] Update the message on (per )
Small and very minor documentation note: If this has landed, we should adjust the #[mustuse] attribute of to keep symmetry with . Currently the hint on is "if you intended to assert that this has a value, consider instead", whereas the one for is longer and more verbose: "if you intended to assert that this doesn't have a value, consider ". Once this methods are stable we should change the latter to "if you intended to assert that this doesn't have a value, consider "_.
yes, that's the third item in the checklist here.
Oh, excuse me. Note to myself: read the issues completely before commenting. Sorry again.
I guess it could use more in-repo callers? I do see that and are using , at least. Grepping for shows a lot more possibilities, but maybe some of those would not like the code to the value in the panic message.
I'd love to see this go to stable.. I'm having to do this: And I'd much rather do
In the stabilization PR it was raised that may be more appropriate and flexible in these signatures than . Do potential users here have a preference for either?
You can use if you need a reference. Also, we should be consistent between our functions ().
I'd like to see be completely parallel to . has unwrap(self) and unwraperr(self), so I think should likewise have unwrap(self) and unwrapnone(self).
what would return? exists because the type still contains a value. But None doesn't.
would return nothing if it is None and panic if it is Some. Here is a practical example of how I would like to use it: EDIT: Oops! I didn't realize I already had this example above: (More examples here: )
It could be called , since there's nothing to return, but calling it is nice to mirror .
My two cents on this topic after finding myself wanting to use these APIs: Besides , I do believe there is value to add and methods. I do agree with the argument that we should avoid adding new methods to std, but these seem pretty natural to me. As for the name of the methods, I believe is better than because it parallels with and pairs with . It is not an absolute incoherence to "unwrap" a to a (it seems quite logical, actually). Also, one already knows and methods, so it is a smaller cognitive burden to find (and memorize) all other similar methods, and we wouldn't open a can of worms by creating a new "group" of methods. That said, I would either stick with and or remove these methods altogether.
I'd argue for expectnone and unwrapnone as standard. Having them as optional "features" is a strange limbo state. That creates confusion as to whether they're staying around or going away. Using "assert!" around an expression which must be executed for its side effects seems poor form. My main use case for this is for collection "insert" operations where failure is not expected and should be fatal. It's tempting to write let _ = (k,v); but it's better to check. A concise syntax for checking is a good thing in this situation.
fcp close We discussed this in the Libs meeting and decided against stabilizing these methods in favor of . The reasoning was that all this method can do is either succeed with a or panic, so is an assert, but that's already handled by the macro.
Team member has proposed to close this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [x] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), 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 for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
When I read the prior comments, I see more people in favor of merging than closing. In particular, I noticed a rebuttal to the rationale for this disposition that was thumbed up 10x (). I interpret this as strong consensus against the disposition. Since we’re entering FCP, I want to briefly summarize why I would like to see this merged: The proposed API is tiny. It doesn’t introduce new concepts. It adds consistency between Option and Result It’s unlikely to break or become obsolete We’ve identified multiple practical situations where this is immediately useful to many people (unit tests, inserting values that should be unique into containers (), method chaining) EDIT: My first link was not the comment I intended to link
feels much lower cognitive complexity (two "chunks" in a linear reading style) than which is three chunks in a jumping-around reading style. Especially important if it is a part of a larger statement.
If anything this is much closer to "Consensus Accept" and is certainly nowhere near "Consensus Reject". I see lots of people articulating in the positive direction and very few articulating negatives. Why is this being shut down? I'd like to see some real reasoning for closing this given that it actually improves consistency with Option and Result, clearly had users even within the Rust codebase itself as well as in the "real world", and doesn't seem to have garnered any negatives. In addition, I would certainly like to see someone address the "Using "assert!" around an expression which must be executed for its side effects seems poor form." comment which is a substantive negative comment about the proposed alternative. I would also argue that the "chained" form of this expresses intent more properly than a random assert sprinkled in the next line. This is going to allow better optimization. I can also tell you in the embedded space that it is not rare that we just blast assert!() from the codebase while leaving the more "intent" panics expressed like this alone. However, I generally try not to push "What gets done in embedded" too hard as we do very nasty things on a daily basis. :) Thanks.
The final comment period, with a disposition to close, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.
Ok, how do we get a human review of this? :)
I was a “weak merge” voice earlier, but after considering, I’m a “strong merge” voice. For one reason: It encourages a modern chained-monad functional programming style, which was also one of the considerations in the postfix syntax decision, which I think has worked very well.
Why did this close given the comments flying around with no response?
It didn't close yet. The bot just comments that 10 days after FCP started. Marking this as waiting-on-team.
Here's my view on this: The main purpose of is to get the value held inside, which has no other option but to panic when the value isn't there. Panicking is not its main purpose. This is similar to indexing an array: will panic if the array doesn't have a fourth element, but that bounds check is not its main purpose. For example: Here, if you were not interested in the value, but only in the panic, I'd expect that to be written as: And not: Even though this code would also panic on the same conditions. I agree that method chains can be a nice way to write things (and am a fan of postfix macros and keywords), but I don't think should get a special method any more than or or should (e.g. or or ). (Maybe if we ever get postfix macros, we could have a . But that's a whole separate discussion.)
on the other hand does have value over : It puts the value held in the in the panic message. That's something couldn't do by itself.
I would expect that unwrapping is the main purpose of any function, and that the value held inside the wrapping is returned as a secondary consequence. Getting at this secondary consequence is often (but not always) the reason why we unwrap, but best not to conflate that with what unwrapping is. Further, is not any ordinary enum discriminant, it explicitly represents a container — a wrapper — that contains no value: Type represents an optional value: every is either and contains a value, or , and does not. It makes perfect sense to unwrap any container, even if that container contains no value. Reifying this into a return value can only yield ("used when there is no other meaningful value that could be returned"), and is therefore known a priori, but this is a red herring. Thus, has a pure symmetry with : Unlike , is not useful for its return value, but it is useful for different secondary consequences of the act of unwrapping — secondary consequences that we already use often with and . So we would expect by symmetry from that would be present and usable in the same way.
This confuses me. Removing the wrapper around a value is unwrapping, right? I'm not sure what you mean here with 'unwrapping' if it's not to get to the value. The documentation explains that has no value. It doesn't wrap anything, there's nothing inside of it. Only wraps a value. (Unlike , where both and wrap a value.) But you can't unwrap something if there's nothing to unwrap. An contains (even if is ), but doesn't contain anything. So do you think should also have a , that would cause a panic for empty vectors? Yes, that's exactly what I meant in my comment about using only for its panicking side effect. It'd work, but isn't the purpose of that method/operator.
One possible alternative that may be interesting for more than just would be to have and/or methods on as a general-purpose chainable assertion.
The panic when we does not happen because of any inability to "unwrap a ", but rather because the (implicit) assumption of a variant did not hold. is singularly unique because it wraps (abstracts, contains, represents) the concept of there being no value and no type inside, which is a representable thing when unwrapped: . You can unwrap an empty . I think it would be coherent for to unwrap to an owned slice, if such a thing were possible. Note that unwrapping an empty in this way would not panic, has an obvious correct result, and represents something other than the complete absence of value and type. If the 's did not exist, then it would panic, but of course there is no way to make that method call. However, is callable, and indeed panics, because means "Assume variant and unwrap that, otherwise panic", as above. Unwrapping (rather than assuming and unwrapping that) is a symmetric and coherent idea — it succeeds and returns . Yes, that's exactly what I meant in my comment about using only for its panicking side effect. It'd work, but isn't the purpose of that method/operator. If any that doesn't use the resulting value is a misuse of the language, we've got a bigger problem. is an ontologically coherent and useful method that means "perform the unwrapping of the expected variant". It is an imperative statement that performs an action on a value, and the existence of this action is implied by symmetry from the existing actions on and . In other words, I am interested in the action of unwrapping, not the panic, so this is not an assert, despite the isomorphism.
I agree with closing this. Using a normal assert for cases like these is more than good enough. I also find the notion of unwrapping nothing quite strange and don't see a lot of value, if any, in adding it.
It's no stranger than a function returning nothing. There's prior art in dataflow programming: The production of the value (even though in this case it is the unit value) is the thing of interest, the purpose of the function. Same with : The production of the value is the result of interest, not necessarily the value itself (or whether it exists). It is the reified essence of the method completing successfully. The value behind the reified value of nothing is that we can think of more of the code as a pipeline, where the production and consumption of values is chained into meaning. An assert is a gate, an interruption in the flow. Postfix asserts would be an improvement, but they still come from the mindset of the error case, another code path to put on our mental stack. is an affirmation, a declaration that we are on the happy path, a statement that everything is flowing smoothly, that the container contained the expected contents, and any issues will be handled by the strict typing of the pipeline's components. (With our choice of error handling methodology being reflected by which components we choose.) This is the same idea behind the operator — an intent to perform an unwrapping, a confirmation of happy path, a typed dataflow — with a preferred error handling strategy. I'd love to be able do things like as well for the same reason. Sometimes all you need is a line number to track down the problem. (See also: )
I understand what you're saying. I just don't find it compelling. Well, I suppose we will have to agree to disagree. A function returning nothing is a routine and well understood thing. Unwrapping a "none" value is not.
There are comments with lots of +1s from before and after FCP. Can we please get responses to those before closing this?
Do you agree that the existing methods on and suggest the possible presence of and specify the exact meaning of , and that the which it would return meaningfully represents the result of an unwrapping?
No.
Here's what's stable today: Every character of the fourth quadrant and its implementation is exactly specified by the rest of the chart, it performs a useful function, and it is missing from the language. I and multiple other people ended up here because we tried to use it. It is a phantom method that exists as a reflection of the other three, whether or not it is written down. If it literally performed no function, was incoherent, sure, don't write it down. But it does do something, and useful at that, so if it's not written down, it's just .... missing.
We're just going in circles. I already responded to that. A missing quadrant on its own is uncompelling to me.
Is there any room in for stuff that a fair amount of users want, but the libs-team would personally not use? I think there's no real cost to adding these small methods, so the argument against seems mostly aesthetic to me.
The cost is inherent in increasing the API surface. No code is better than no code, as they say.
I haven't been arguing from the stance of whether I would use this or not. I've been arguing from the stance of whether it's a good fit for std. There is definitely a lower cost to adding methods like these on to existing types when compared to other types of API additions. And we have historically had a lower bar for such methods. But the cost isn't zero. Unwrapping an absent value doesn't cut it for me. Changing our policy to rubber stamp APIs based on some upvotes doesn't sound like a good idea to me. Utility to folks is and should be a factor in our decision. Folks have made their case. I personally remain unswayed.
Based on your "No." response above, I'm concerned that I may have failed to even express my argument in a way that is understandable. I believe these are factual statements, above opinion and potential for disagreement. Could you be specific about where you disagree?
It may also be worth reiterating the original motivation from -- has a rather verbose suggestion because there's no simple method to make that assertion. I think using when you expect is really beating around the bush. If we (collectively) really think that it's best to wrap this as , then should say that.
If you intend for them to be facts, then I probably agree with them. Otherwise, my disagreement lay in the fact that I think doesn't make a lot of sense. And the presence of certain methods on Result do not necessarily specify the behavior of unwrapnone. For example, I think it's just as reasonable to assume that since "none" represents absence, that there therefore is no analog to unwrap_err for Option. That is, the quadrant missing in your table is totally reasonable and not unexpected from my perspective.
I'm really not super sure what the value is in arguing with core maintainers by claiming your statements are above disagreement over something that maybe had a decent amount of a support at one point but now no longer does.
My intent is to improve the Rust language by identifying differences in mental models of how the language is structured that can be resolved through discussion. That and exist in the language is beyond disagreement — it is a foundation on which we can build our models and our interactions. I'm trying to understand where our shared foundation actually is. Thank you. It sounds like you believe that a value should not be able to be "unwrapped", is that correct? Specifically, I can't find a place in the Rust documentation that defines what "unwrap" means in an abstract sense, and surprisingly, I also could not immediately find a reputable direct definition of "unwrap" in a computer science sense that applies to the Rust usage of the term. I think it would be appropriate and generally helpful to clearly define the term, as Rust uses it.
I don't really think that support actually went away. People with real uses cases are still here. And the language developers have a very different view from the end users. I am in the camp of thinking about the "unwrap machinery" rather than the value. From the developers view, there is no difference. However, if that is really true, should that not be locked down? Or is this semantic being "reserved" for some reason? Is there an implementation complication? While there is value in "minimal API" there is also value in "specified API"--especially when the API to be looks like a hole rather than purely a new feature. This feature is useful enough that when the language has the ability to support it--even if horribly badly--it will reappear, it will gain some level of popularity, and the developers will not have control of the semantics. As for embedded, I can almost certainly cough up a chain of operations that can't be broken apart into that separate assert but I probably can't limit it unwrap_none()--something else will bite first. Memory that can read but not write, write but not read, partial ownership of a byte, etc.--a lot of things get expressed in weird ways because we have nasty edge cases and can't always be converted to "obviously equivalent" alternatives. However, if you're not convinced by earlier examples, you'll just call mine tortured (and, to be fair, that's likely true). At the very least, the folks developing this have broken radio silence, so we now have some rationale. However, please do expect some pushback when the rationale is "We don't think this is useful enough" (subjective aesthetic objection) as opposed to "This has unclear, difficult to define semantics." (objective technical objection) Thanks.
Oh my, it's worse than I thought:
It appears that the controversy is mainly about . Could we stabilize just first?
just curious, do you feel that is harmful or is it just that is better than nothing? I do think is much better than nothing. (I can use to achieve , it's just less "nice") That said: The core team's original rationale for closing was "all this method can do is either succeed with a () or panic, so is an assert, but that's already handled by the assert! macro." The consensus (just judging by upvotes and comments) during FCP was clearly for merging. After FCP, the core team has mostly reiterated the same that was put forward at the start of the FCP and it sounds like they still want to close. I realize this isn't a pure-numbers "vote" but I would like to know what is so disliked about these tiny methods that makes it worth going against the clear consensus.
all this method can do is either succeed with a () or panic, so is an assert I think there's a subtle error in this statement that affects judgement — has an equivalency to a sequence of + assert, but it is not an assert. Multiplication is equivalent to a sequence of additions, but it is not a sequence of additions. Equivalency alone is not a meaningful standard, because we can write equivalent code to all of .
That's an interesting point: Is it documented somewhere that assert!() MUST return ()? Or is that simply an accident of implementation? If assert!() becomes able to function in a postfix way for chaining, that will change, no?
If postfix macros are implemented, both and are not required anyway, because we could just , , , etc. the same way.
Ah, good point. Shame that postfix macros have basically been stuck for four years now with no end in sight.
I just want to comment here, as someone who doesn't have a super strong opinion in any direction, and is not on the libs team: at the end of the day, procedurally speaking, the libs team is empowered to make decisions about what goes into the standard library. We do not develop Rust based on comments, upvotes, or reactions on GitHub issues. If we did, there would be no reason to have the teams. The reason (well, one of many reasons that) we have the teams is that sometimes, hard decisions need to be made. This means comments characterizing this as "most people want this method, therefore consensus is closer to merging than not" is a category error. Many people may want this method, but for the purpose of accepting or rejecting, it is the opinion of the teams that counts. If literally every other person in the world is and the team members are , then the consensus is . On a personal note, I sometimes disagree with teams decisions too. But, not every single person will ever agree with every possible decision. That's just how things go.
Okay, with my legalese hat off: Is there anything blocking people from implementing this method themselves if they want it? Normally what happens in these scenarios is that people do so, make a package, and then then the ecosystem popularity can help inform decisions. As a concrete example of this working, before Rust 1.0, . I thought it would still be helpful, and so I made a package for it: Eventually, it was demonstrated that this was useful enough to bring back, and so it was: If people feel very passionate about this API, building such a package might allow them to get the benefits they want today, without needing to convince the libs team of anything. It also may help build the case for an eventual reversal of the decision.
Yes — In practice, it's an ergonomics issue, and having the overhead of another crate and having to it everywhere negates the ergonomics benefit. That does not sound... inclusive and empowering. I'm not saying there should be a direct popular vote, but I think something of the spirit of Rust is missing here.
Another way to go about this is to possibly add a .assert postfix completion to rust-analyzer. That probably makes sense to have anyway.
This partially addresses "writing" ergonomics, but not "reading" ergonomics.
The problem with this method, is that it opens a whole can of worms where you'd basically establish this as a new Rust pattern where every method returning a boolean should also have a / variation. ? Better add / too. / / . / / and so on. You might argue those make sense, but you'd open up a huge can of worms leading to not just a minimal addition to std, but quite a lot, with arguably very little advantage compared to an assert other than it being in postfix position. It honestly makes a lot more sense to push the postfix macros RFC more rather than establishing this new pattern.
Hmm, a crate that implements a whole bunch of useful postfix traits on many standard types is not a bad idea; it would amortize the overhead of to the point where it could work. (I note that steveklabnik's example actually contains four functions, two of which are aliased to alternate names as well.) For reference, I proposed in a detailed concrete definition and mental model for the word "unwrap" that is coherent to existing usage and would not imply its presence on , , etc.
devil's advocate, most of the people here are arguing for this because of the "hole" implied by and . However I do find this argument compelling. If such a crate existed, I can nearly guarantee the vast majority of its users would be using solely for /, which kind of defeats the point of such an amortization, because then they'd be bringing in a whole bunch of other code. You could perhaps deal with it by using features, but I don't see much point in that.
I'd expect the methods to be on not for that example, , which feels reasonable to me stylistically. I'm not suggesting that those methods be , but I do like the concept of postfix assertions
, , , and do not have or functions, so I don't think it makes as much sense to put or on them as it does for and . (Result already has them) I understand and respect that this is not a "vote" and that the lib team is free to form their own consensus among themselves. That consensus may differ from the consensus of those who participated in FCP. I was expecting a similar process to be followed for FCP as was stated . "there should not be a strong consensus against that position outside of the subteam" Does this not apply because this is not an RFC? That being said, if the lib team holds the privilege to overrule what the community is saying, I would hope that it is coupled with a responsibility to consider all feedback from the community (even the +1s) and to maintain a very high bar for choosing when to overrule it. If that bar to overrule was being met, then I would expect the lib team could articulate stronger reasons for why they are against this stabilization (and to respond in more detail to the comments left during FCP that were in favor of merging.)
I read it more as an assertion on the underlying value, so you're asserting that returned true, similar to (in my mind) asserting that it's Some and returning the underlying T. My only usage of is as an assertion that the entry I'm inserting into HashMap is a new entry not a replacement, so maybe there are better ways of expressing that, like using the Entry syntax.
is_empty() has no side effects, so I don't mind so much putting that in assert!(). Options are frequently returned from things that do have side effects, similarly to result. (such as inserting into a hash map)
My theme in this comment is "I was not clear enough before", heh. My bad. Sorry, I wasn't clear before. I meant technical reasons. Sometimes, things like coherence rules prevent an out of tree implementation. That doesn't mean this ergonomics thing isn't real, and you're right that there's a tradeoff here, but it is fundamentally possible. Different people will way this tradeoff differently, but it being possible matters. It is. However, What I am trying to do here, and again, I wasn't super clear before, so my bad, is provide you all with the way to make your arguments in the strongest way. The fact of the matter is, presenting this GitHub thread as "all feedback from the community" is also a category error. Is it a signal? Yes. Is it a particularly strong one? No. The reason being that, first of all, we're talking about a couple of people in the grand scheme of things, but also, the list of people in this thread is heavily biased towards people that want to see this . That is, after all, the point of this thread. So you can't really take this that strongly. You also have to couple this with the responsibility of the libs team, and that is to steward the standard library, forever. The default is always no. We can add things, but never remove them. This means that there is a high bar for justifying adding things. But also, at the same time, sometimes, reasonable people disagree with each other. And sometimes, this changes over time too. I remember being really sad that postfix await was chosen by the language team. At the time, I thought it was a huge mistake. I campaigned against it very hard. A significant amount of the community (and that decision had a much broader audience than this thread) agreed with me (and others). In the end, though, now that I've lived with that decision for a while? They were absolutely right, and I was wrong. It was a tough call, and it wasn't popular at the time, but it was the right thing for Rust. This doesn't mean this is always the case; I bring this story up partially because it's sort of a counterpoint to the story I laid out earlier about , where a decision was reversed later, because that was the right call eventually. Taking feedback into account is being inclusive. That the community is privy to this process at all is radically different than many other languages. Making decisions that may be unpopular isn't inherently un-inclusive. (that leads into this time) This is a good procedural point, for sure. I do think that some more elaboration and a direct response to these points would be nice. At the same time, we all have to remember that we're all human, and we're all here for the same purpose: to make Rust better. The teams have so, so, so much work on their plate, and sometimes this means that a particular thread may not get as much love as any given individual may think it should. I do generally agree that the more heat a thread is, the more communicative the team should be about the reason it made the decision that was made. But I will also note that this hasn't actually been closed yet. That opportunity is still here. Well, there is also this right before: I personally think that that sentence is a bit strongly worded, but also, yes. The bar gets higher the bigger something is. This is already a lot of stuff for a very small API. (To be honest, other than the fact that I am a governance nerd, and a general desire to de-escalate threads) I am not even 100% sure why I am spending so much time on this thread; I don't feel strongly that this should be included or not included, but here I am writing a ton of words on it...) Anyway. That's just my thoughts. Again, I am not on the relevant team here, so I'm just a random commenter as much as anyone. That's how I see this situation. I will probably leave this here :)
Quite. The pile-on in this issue is rather unedifying. I had hoped we would treat our libs team with more respect. Most of the individual messages would be OK by themselves, but I think the cumulative effect is not. If you just want to +1 something, do that. Personally I like the idea in this MR but if the libs team don't like it there is no reason it couldn't be done in an extension trait as you suggested. Maybe someone would like to enhance to have this feature? boolinator already has an extension trait for providing . It seems to me like it would be a natural home for an extension trait for providing etc. It could also usefully be enhanced with on .
A) Leave the "pile-on" and commentary for elsewhere, please. B) As for edifying, I have seen quite a few more substantive options and comments about alternatives after the fcp than before it. Had some of these options been mooted and discussed prior to simply just "fcp close", my guess is that a "reject on aesthetic grounds" would have gone over much better. Had some of these things appeared above the "fcp close", I, personally, probably wouldn't even have commented. C) Until some of us started jumping up and down, there was radio silence from the libs team about why something which seemed like a shoe-in had a single in-private meeting and suddenly was dead. People got surprised and that's not going to go over well. And I think both sides got surprised. The people in favor of this issue got surprised when it suddenly went from "looks like its going in" to "reject" very suddenly. And I suspect that the dev team got surprised that people cared about this more than they realized. wrote: You are claiming that you should ignore feedback simply because the people involved are actively engaged in the topic at hand? Maybe you adjust your Bayesian prior down some for enthusiastic engagement, but you don't just set it to zero. Rust issues with negative feedback are not at all uncommon, and if that were the case here people would certainly be pointing to it for reinforcing their position. So, positive feedback, lack of negative feedback, etc. really is signal rather than noise. What you choose to do with that signal is a different matter, but you can't just dismiss it as not being signal.
I specifically did not say that it should be ignored, or that it is not a signal. I said that it is not a strong enough signal to be presented as some kind of "community consensus."
As I said above: This issue being turned into a debate about procedure is incredibly draining. I think has roughly described my understanding of our governance structure. I would like to re-emphasize here that the legalese is that the teams do not have to make decisions based on voting by others or any other such thing. However, in practice, teams do take community feedback into account, because that's what conscientious people in a healthy project do. This does not necessarily mean that a few vocal folks along with a ~dozen positive emoji reactions will get their way in adding an API though. There's a lot of room between "we take your feedback into account" and "you aren't listening to upvotes and a few of us emphasizing that we have strong opinions on this matter." In terms of emoji reactions, I will just say that I personally put very little weight on them. They are a signal for sure, but a weak one. All it takes is for someone to link a github thread on twitter or some other social media web site to skew that signal in ways that make it nearly useless. (And this doesn't even need to be done maliciously. Sometimes you really do just want to get more eyes on something.) And in general, it's impossible to know when that has and hasn't happened. This doesn't mean all emoji reactions are useless. But in terms of their impact on my personal decision with regard to std APIs, they at best say, "take a closer look." In terms of the folks voicing opinions in this thread in favor of this API, I have engaged with them. I've explained why I found the arguments in favor of this API to be uncompelling. I also agree with commentary on the matter. An argument that I don't believe I've addressed head on is the purported ergonomic benefit to this API. I personally don't see much if any benefit to this, and even if I did, I'm not sure it would out-weigh the downside to adding this API. In particular, I find the notion of unwrapping a value weird and surprising, for reasons I've already covered. Now, in terms of the FCP surprising folks, I went back and looked it over. And I can see how it came across that way. I can't see any dissenting opinions before the disposition to close was proposed. Indeed, the libs team really should be presenting their argumentation in favor or against before calling an FCP disposition so that others have time to respond. I personally wasn't at the libs meeting when the disposition was decided, but my guess is that this was considered such a small API addition that dismissing it without much ceremony wasn't given much thought. So I agree that there were perhaps surprises on both sides here. Back in the old days when the libs team was first formed, we actually had a stronger habit of discussing new APIs in our meetings (often debating amongst ourselves) instead of having the debate openly and asynchronously on the issue tracker. This wound up causing problems very similar to what we're seeing in this thread, and it was why (I believe) Aaron Turon pushed us towards not debating the APIs themselves in the meetings. But rather, having that debate on the issue tracker. I think since then, we have historically done that.
As a general comment to those who might be unaware: Please note that none of us on the library team work on this full-time, but sometimes a few hours or a day a week. We usually have a meeting once a week which is also the moment in the week most of us spend some time on issues like this. Only six days have passed since first comment on this thread, so talking about 'radio silence' after just a few days feels a bit out of place. Things can take quite a bit longer in Rust, being run as an open source project by volunteers.
Reading this thread and from my own experience, it seems like much of the disagreement here relates to side effects in assertions. If Rust already has a position on that (not just what's legal, but what's idiomatic), then maybe clarifying that would go a long way towards obviating the need for this? (Specifically: I got here from the same use case that seems to have brought other person here. That last comment, plus express doubt about side effects in assertions (which is also how I felt coming into this). On the other hand, that side effects can be part of assertions seems to have been and was echoed .) The only technical reason I know to avoid side effects within assertions is that in C, this was likely to result in those side effects not happening in non-debug builds. [Edit: I've seen this behavior in non-C languages or libraries as well.] I think this idea permeates a lot of folks' understanding, not just people who write C, but I don't know of another technical reason to avoid this practice. Having been excited about this feature, I'm coming around to the other side: the awkwardness in semantics around unwrapping a is about the same as the awkwardness I felt about an assertion with side effects. If Rust already embraces the latter, then I have no need for these functions. Still, the evidence in this thread suggests that if assertions with side effects are not just legal but idiomatic in Rust, that could be clearer. I think it would help if the messaging around assert was more explicit about both side effects and being different from C (or "other languages", if that's better). Since a main use case for this issue seems to be , an explicit note in the docs recommending this pattern might make people more comfortable with it. (The example already does include an insert within an assertion. But it looks like code that's testing or demonstrating the functionality. It's not obvious to me that it's saying "this pattern is fine in production code".) Maybe there are other ways to better communicate this idiom to people too, but this is starting to get off-topic.
Quoth That's fair. But please note that (both of whom commented earlier than me), and I commented once over the 10 day FCP, waited patiently for a response during a time while the team actively was ripping this out of the Rust codebase, and I, personally, didn't start jumping up and down until the bot said "The final comment period, with a disposition to close, as per the review above, is now complete." If the granularity of the team is 7 days (perfectly normal), then the FCP should probably be adjusted to 2 1/2 weeks instead of 1 1/2 weeks to reflect that reality.
I'm not sure what you're referring to. The stabilization PR was closed, but I haven't seen any "ripping out". But you did get me to go look for usage in the compiler. I don't see any , but there are 14 :
(Sorry, I don't mean to pile on this heated thread, but I realized another valuable thing I wanted to point out...) The methods also require , and any unexpected value is printed in the panic message. So if you want to completely replace these methods with assertions, you would need to save the value so it can be referenced twice, like: Or replicate the actual method implementation: rust if let Some(value) = some.expression() { panic!("your message: {:?}", value); }
So in that case would it maybe make sense to simply only have .expect_none() instead?
also includes a message, Option::unwrapnone()SomeDebug` value.
We discussed this issue again in the library team meeting yesterday. Thanks for all the valuable feedback and insights. We discussed the arguments in more detail. However, we still stand by our decision to not stabilize these functions. Our main arguments against stabilizing these are: is not the right name, as there's nothing inside a to unwrap. As a method that only asserts something, it doesn't fit any existing pattern. It'd be a new pattern that would also fit things like , , , , etc. This is not a direction we want to pursue. (Or at least not without an RFC.) already shows the value inside the in the panic message. (Note that unlike in C and C++, s in Rust are unconditional and also included in release builds.) This is very true, and we should be better at not keeping unstable features in such a state for a long time, as it clearly causes confusion. We will more actively try to track unstable features and make sure they are either removed or stabilized within reasonable time. We're sorry we might have given the impression this feature was close to stabilizing, causing surprise when that turned out to not be the case. During our discussion, the idea of an was brought up. In examples where panicking on a makes sense, the is considered an error. So, a function that would convert an to a might make sense in some of those cases, such that that can then be handled in the usual ways, such as by unwrapping it. We didn't discuss this option in detail, so there's no concensus on this yet, but it might be an interesting direction to explore separately from unwrapnone/expect_none.
FWIW, I really like(d) these methods -- see for how removing the methods make the code either harder to read [] or more verbose. [] I consider it bad style for to have side-effects. The intention of an assertion is to check that something holds true; as such I expect the check to be side-effect-free. I know that assertions are always compiled in Rust, but just because I can rely on their side-effects always happening, doesn't mean I should.
In all those cases it's about panicking on ing a duplicate key. It makes me wonder whether should've returned a instead of using to indicate the duplicate key error. (Not that we can really change that now.)
Indeed, this is really my primary motivation here: I think there should be a nice way to say "insert a key and assert that it's fresh". did exactly that. with side-effect doesn't qualify as "nice" IMO (see above).
I've personally never thought of asserts with side-effects as being bad style in Rust.
Having a complex expression in an assertion macro might not work well with tools like . Although already lexically identifies , I am not sure if it is compatible with macro aliases. This is offtopic from this issue though.
I opened for the issue.
was stabilizing just expectnone discussed? Many of the objections to unwrapnone did not apply to expect_none.
Yes. The argument of not wanting to have , etc. still holds. We also don't want to break the symmetry of /. (It's also interesting to note that the methods would probably not have existed if happened sooner. Before , you wouldn't be able to distinguish different calls from their panic message. with a message helped out with that.)
asked elsewhere I'm not sure to say here beyond what I said : when I read code I like to consider as "executable documentation", it says "some thing is always true here" and backs that statement up with a runtime check. A comment can be removed without breaking the code; is a stronger form of comment but still more a comment than actually part of the behavior of the function, as far as I am concerned. I know, and I've had trouble reading such code in the past.^^ I view as having a semantic connotation that goes well beyond a panic in an if, so I don't view the two as equivalent.
For people wanting chaining, I think there's always .
Here's an alternative solution for :
True, although it requires . Maybe we should have ?
Thank you. That is a good addition. TBH I still see value in . I don't find the arguments against it particularly convincing. But I don't think discussing that further in this thread is going to be helpful or fun. To those in this thread who want : should I bother with providing this as an out-of-stdlib extension trait somewhere? Would you rather have it as a part of an existing crate (and if so which one), or a crate of its own?
Yes, I think so! I was about to make a PR for , but then got distracted by . :)
great! FWIW, I used plural to go with the existing macro.
It's not plural, I think, it's the third-person-singular-suffix-"s": "X matches Y".
sorry, you're right. It doesn't help that "matches" is also a plural noun. Still, we should try to be consistent.
implementation:
I hope it is fine that im writing here. I just stumbled over this api and was wondering about the constrain. I want to use on a generic option but dont care about the value itself, I just want to give some context msg why a is expected. Since this is still an unstable feature I thought it makes sense to share my opinion with you.
was removed recently, see and .
My god, what is the harm in adding and ? So many people are asking for this very API because they find it useful. People here seem to completely miss the idea behind using it. It is obviously not about unwrapping to get some value, but about panicking when it is not none. maybe "unwrapnone" sounds a bit odd, but "expectnone" is very clear. Also "unwrap_none" makes sense when the wrapper is empty. Having to wrap an Option in an increases code complexity when you are mostly using functional adapters which chain behind oneanother.
This makes me once again wish that was called or , because every is an assertion. As assertion that also returns a value, but an assertion no less. (Making things that usually don't return something, into expressions that return something, is a common thing Rust does, so assertions that return values should not be surprising.) Then we would call this here and nobody would be surprised by it. Right now we are in the awkward situation where some assertions on can be written with nice method call syntax, but others cannot. And yeah, I have run into the same iterator usecase as and used to use there, until it got removed, which made me a bit sad.
Sorry, I deleted the comment you're referencing (where I mentioned it would be nice to be able to do ) since I felt my contribution wasn't valuable enough to be worth essentially re-opening discussion. I saw the wisdom of the lib team's argument when I noticed I'd also then want for some other types, and yeah, I could just do in that case.
which obviously uses way more characters than and requires you to bounce back to process the logic of the line. I just don't see the value of not having a method like this. I don't find it misleading or anything, there are always different ways to do things, doesn't mean you don't want to take shortest paths. Not having this just makes using the language harder for no reason.
I’m still doing this, over and over. The libs team implemented an alternative in response to it 18 months later, it hasn’t moved. So I’m stuck here doing this over and over. (Additionally, I’m still disappointed at how this FCP was handled. Not just the result, but the way in which the libs team executed the process/engaged with the community)
The problem is that this issue is fundamentally an XY problem, where the real problem is the lack of postfix macros but we tried to solve this problem by bloating the standard library instead.
I disagree with that, unless you think 'unwrap' and 'expect' are also just bloat and should have been postfix macros (but then I don't know which macro). But I think I will use "x.okor(()).expecterr(...)" for now for the iterator case (where one wants to check that the last next() returns None), that is at least almost symmetric with the earlier calls to next that should return Some.
yes, I think and are bloat as well. They should have simply been
This statement doesn't really make any sense. You aren't unwrapping the , you're unwrapping the , which may or may not contain thing. Unless the libs team or someone else explicitly specifies what 'unwrap' to be consistent with that argument, the analogy used earlier holds: unwrapping a present and finding nothing was still unwraping the present. I'm skeptical of this at its face value. The notion of is fundamentally the concept of "unwrap this container, expect this valuedness, then return what was inside": ---This is simply not ergonomically consistent with how one expects to assert the valuedness of the container enums. Unless there are plans to deprecate the panicking APIs, then this semantic inconsistency will continue to draw new users to this issue, only to be disappointed by unclear and inconsistent logic from the rust teams. This seems like skirting around the reality that the expectation of a semantic way to inline-assert the valuedness of a container has already been proposed in this issue. You certainly can introduce the function discussed in this thread as an entirely new unwrapping API separate from the existing unwrapping APIs, but it feels like arbitrarily beating around the bush of this issue. Really, this is functionally another inline syntax for . Overall, reading this, I wasn't particularly satisfied with the responses, justifications, or in general the way this issue was handled. There was next to no communication while the issue was being reviewed, followed by subjective arguments, followed (finally) by an actual discussion of the logical reasoning of the libs team. It really just seems like the team that voted on this doesn't like the idea of unwrapping an empty .
Quoting Which would be fine, except that 5 years on we don't have a solution for X OR Y. And none of the alternatives mentioned to solve this have moved even slightly over 5 years. And, if postfix macros didn't get resolved given their usefulness for "async" which everybody on the core team was totally obsessed with, they are never going to move. So, I'm so glad you maintained your vaunted "language purity", but, in the meantime, users of the language are still stuck exactly where they started. That's fine. Many of us can take a hint and understand that it is time to move on from Rust.
To contrast, I find there is value in a clean code base and principled design. But I think pragmatism is one of the most important principles which should drive language design. And I personally don't really have any validated reasons on my list about why or would be bad to have. If there is any reason why the existence of these functions could reduce the quality of the Rust language, then maybe we should start talking about those instead of why it might be a good idea to have these. They are obviously useful, so if there are no reasons why this should be blocked it should simply be to a library and eventually stabilized.
Sure. To unwrap the , we already have a method: . In an , there is nothing to unwrap, so it panicks. is about getting the contents of the out. But isn't about getting the contents of the out, because there are none. Sure, you can represent that as , but if you want a , you don't need to call this method. The only reason you'd ever call is as an assertion, a conditional panic, not as a way to access a value. So, it'd basically be a or . So then the question is whether we want such methods. I don't think we do, because we also don't have anything like , , or , and so on. All of those assertions you'd write using an assert macro or a panic inside an if. I don't think there is enough reason to give special treatment. I don't think Rust is very "pure". I also don't think users are "stuck". There are many simple ways to express a check for , so I doubt this is really a blocker for anyone. Maybe there's even a crate with an extension trait to add to various common types. Many reasons have been given in this thread. I'm not sure what I can add at this point without just repeating things that have already been said.
As I highlighted , we're all just volunteers working on the standard library next to many other tasks, so while we'll try our best to communicate everything as clearly as possible, it sometimes takes longer than you might hope for. I'm afraid that API design will always be based on subjective arguments. There's no objective right or wrong way to design a library. I'm not sure what you expect from us at this point. Many arguments have been given and thoroughly explained over the past few years, and it just seems like we're going in circles now. :/
The unexpected. I mean, literally, the to mirror already available

Discussion:
use crate::iter::{FromIterator, FusedIterator, TrustedLen}; use crate::pin::Pin; use crate::{ <del> fmt, hint, mem, </del> <ins> hint, mem, </ins> ops::{self, Deref, DerefMut}, };

} } <del> impl<T: fmt::Debug> Option<T> { /// Consumes `self` while expecting [`None`] and returning nothing. /// /// # Panics /// /// Panics if the value is a [`Some`], with a panic message including the /// passed message, and the content of the [`Some`]. /// /// # Examples /// /// ``` /// #![feature(option_expect_none)] /// /// use std::collections::HashMap; /// let mut squares = HashMap::new(); /// for i in -10..=10 { ///     // This will not panic, since all keys are unique. ///     squares.insert(i, i * i).expect_none("duplicate key"); /// } /// ``` /// /// ```should_panic /// #![feature(option_expect_none)] /// /// use std::collections::HashMap; /// let mut sqrts = HashMap::new(); /// for i in -10..=10 { ///     // This will panic, since both negative and positive `i` will ///     // insert the same `i * i` key, returning the old `Some(i)`. ///     sqrts.insert(i * i, i).expect_none("duplicate key"); /// } /// ``` #[inline] #[track_caller] #[unstable(feature = "option_expect_none", reason = "newly added", issue = "62633")] pub fn expect_none(self, msg: &str) { if let Some(val) = self { expect_none_failed(msg, &val); } } /// Consumes `self` while expecting [`None`] and returning nothing. /// /// # Panics /// /// Panics if the value is a [`Some`], with a custom panic message provided /// by the [`Some`]'s value. /// /// [`Some(v)`]: Some /// /// # Examples /// /// ``` /// #![feature(option_unwrap_none)] /// /// use std::collections::HashMap; /// let mut squares = HashMap::new(); /// for i in -10..=10 { ///     // This will not panic, since all keys are unique. ///     squares.insert(i, i * i).unwrap_none(); /// } /// ``` /// /// ```should_panic /// #![feature(option_unwrap_none)] /// /// use std::collections::HashMap; /// let mut sqrts = HashMap::new(); /// for i in -10..=10 { ///     // This will panic, since both negative and positive `i` will ///     // insert the same `i * i` key, returning the old `Some(i)`. ///     sqrts.insert(i * i, i).unwrap_none(); /// } /// ``` #[inline] #[track_caller] #[unstable(feature = "option_unwrap_none", reason = "newly added", issue = "62633")] pub fn unwrap_none(self) { if let Some(val) = self { expect_none_failed("called `Option::unwrap_none()` on a `Some` value", &val); } } } </del> impl<T: Default> Option<T> { /// Returns the contained [`Some`] value or a default ///

panic!("{}", msg) } <del> // This is a separate function to reduce the code size of .expect_none() itself. #[inline(never)] #[cold] #[track_caller] fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { panic!("{}: {:?}", msg, value) } </del> ///////////////////////////////////////////////////////////////////////////// // Trait implementations /////////////////////////////////////////////////////////////////////////////

fn test_iterator_chain_unfused() { // Chain shouldn't be fused in its second iterator, depending on direction let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); <del> iter.next().unwrap_none(); iter.next().unwrap(); iter.next().unwrap_none(); </del> <ins> assert!(iter.next().is_none()); assert!(iter.next().is_some()); assert!(iter.next().is_none()); </ins> let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); <del> iter.next_back().unwrap_none(); iter.next_back().unwrap(); iter.next_back().unwrap_none(); </del> <ins> assert!(iter.next_back().is_none()); assert!(iter.next_back().is_some()); assert!(iter.next_back().is_none()); </ins> } #[test]

#![feature(unwrap_infallible)] #![feature(option_result_unwrap_unchecked)] #![feature(result_into_ok_or_err)] <del> #![feature(option_unwrap_none)] </del> #![feature(peekable_peek_mut)] #![cfg_attr(not(bootstrap), feature(ptr_metadata))] #![feature(once_cell)]

#![feature(nll)] #![feature(available_concurrency)] #![feature(internal_output_capture)] <del> #![feature(option_unwrap_none)] </del> #![feature(panic_unwind)] #![feature(staged_api)] #![feature(termination_trait_lib)]

let test = remaining.pop().unwrap(); let event = TestEvent::TeWait(test.desc.clone()); notify_about_test_event(event)?; <del> run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No) .unwrap_none(); </del> <ins> let join_handle = run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No); assert!(join_handle.is_none()); </ins> let completed_test = rx.recv().unwrap(); let event = TestEvent::TeResult(completed_test);

// revisions: default mir-opt //[mir-opt] compile-flags: -Zmir-opt-level=4 <del> #![feature(option_expect_none, option_unwrap_none)] </del> #![allow(unconditional_panic)] //! Test that panic locations for `#[track_caller]` functions in std have the correct

assert_panicked(|| nope.unwrap()); assert_panicked(|| nope.expect("")); <del> let yep: Option<()> = Some(()); assert_panicked(|| yep.unwrap_none()); assert_panicked(|| yep.expect_none("")); </del> let oops: Result<(), ()> = Err(()); assert_panicked(|| oops.unwrap()); assert_panicked(|| oops.expect(""));