-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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] Add Option::todo
and Result::todo
#3706
base: master
Are you sure you want to change the base?
Conversation
Option::{todo, unreachable}
and Result::{todo, unreachable}
This has a better chance as an ACP (API change proposal) over at https://github.com/rust-lang/libs-team/issues. |
As an initial reader, I really like My concern with unreachable is confusion with https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/std/intrinsics/fn.unreachable.html (std::intrinsincs::unreachable), which is unsafe, and marks that the code as unreachable, and can lead to UB if the code is reached. |
Isn't it called |
Sorry, I should have checked the version -- I remembered seeing, and using the |
The |
I agree that That is to say, We could combine these a bit to get
In general, I think If |
It doesn't provide much benefit, if at all, and it arguably drags this RFC down a lot. I think `.todo()` is a strong candidate, but `.unreachable()` is contentious and can be emulated nicely with `.expect("REASON WHY UNREACHABLE").`
Option::{todo, unreachable}
and Result::{todo, unreachable}
Option::todo
and Result::todo
I've updated the RFC to remove After reading some comments and thinking over it more, I don't think There are also many more points of contention in the naming of I'm looking into moving this over into an ACP now. I had no idea that ACPs were a thing; they're not mentioned anywhere in the rfc readme :( |
Some further obvious alternatives you can already use:
some code examples for illustration let int: i32 = input.parse().unwrap_or_else(|| todo!());
let Some(arg2) = std::env::args().nth(2) else { todo!() };
// TODO: handle error case
let file_content = fs::read("file.txt").unwrap(); |
i think you'd also want a |
From what I understand, But how is |
IDE could mark .todo differently from .expect (similar to the justification that IDE could mark todo! differently from umimplemented! see https://doc.rust-lang.org/stable/std/macro.todo.html) Also, clippy could warn .todo separately from .expect |
Isn't Like, the whole point of calling unwrap is to say "I'm so confident that this I kind of like the idea of todo, but as folks have said, |
clippy could easily warn |
But it's not. Both compile to an explicit
|
Having some functions doing exactly the same thing except for some artificial "semantic differences" seems strange to me, especially when the motivation can already be addressed to some extent by simply comments / |
It is a bit strange, but I don't think this is without precedence in Rust. All of these arguments could -- too -- be applied onto the One can rewrite all
And the sort of ugly part here is that if the solution for an At the moment, I rarely see Although today you can do all of these things: // this is 27 characters long; no prototype code will ever realistically go through the hassle of writing this
let int: i32 = input.parse().unwrap_or_else(|| todo!());
// This is 19 additional characters and doesn't chain.
// this is useful in Non-Option/Result cases though, where you want to pattern match a single variant.
let Some(arg2) = std::env::args().nth(2) else { todo!() };
// i.e. is not easily possible
let thing = getstate().todo().thing
// And storing important information in comments just never seems to work in practice,
// it falls out of sync with the code and isn't easy to grep for.
// People forget, people don't do it, and there's no guarantee that comment is in english either. |
I'll throw out that this could easily be a tiny crate using an extension trait. Going into STD is a high bar. If this is so necessary it seems to me that one could make such a crate and prove the popularity first. That's not a yes or a no, I'm just saying the obvious thing. I'm sort of meh one way or the other. However to me if it can't become a popular crate and it isn't something that the compiler itself has to help with, then it doesn't seem to hit the bar to go into std in my opinion. |
Sure. I have this functionality in a project at the moment; I can shape it up into a crate. If this crate catches on with this name -- does that block that name from going into STD? I remember hearing some similar issues with popular Itertools constructs effectively blocking the name from going onto Iterator. |
The main problem with |
Awesome. I'll publish my crate for this tonight. |
This is now published as a crate: https://github.com/zkrising/unwrap_todo ( |
I think the unreachable variant has merit too. I often find myself writing: |
Unwrap todo seems great. I remember when I first learned Rust, I only knew
That said, would/will I add a crate just for I've seen a lot of APIs gone into std because they are just convenient to use or express intention. When I saw them stabilized, I first got surprised, just like |
I've been reading all the responses to this and I wanted to point out two things I found sort of interesting: Temporary panicking in prototypes is commonThat is to say, using something for temporary error handling for prototype code isn't a niche thing. A lot of people in those discussions have expressed their usage of something for temporary handling. A lot of people have chimed in with what they do in their codebases for these semantics. I think that implies heavily to me that there is demand for this semantic, but since the language doesn't provide a standard choice, people assign their own semantics ontop of the existing APIs. That suggests, to me, that the semantics I'm describing in the OP are at least "real" -- "unwrap todo" is something that people already want to express, and I'm not just suggesting something nobody uses 😅. People have different ways of expressing itIf the above point establishes that "just panic here temporarily" is a real thing people want, then the rest of the discussion goes to show that there's not an agreed way of expressing it. I've seen, so far:
There's nothing necessarily wrong with this, but there's a lot of talking-past-eachother in these threads; it seems to me like there's not a consensus on what The standard library docs don't seem to take a stance on this either, which likely contributes to this confusion. I think the discussions (at least, to me) evince |
Semantics aren't the same as...I don't have a good word, let's go with conventions. You're suggesting effectively multiple names for the same semantics and that they be so blessed that they belong in stdlib. I would go so far as to also say that panicking in prototypes isn't great either. You can throw Anyhow or similar at many of these problems and then it's just As I already said it's possible to do this as a crate. If such a crate became popular I'd be less negative about this. You're showing that people like to express temporary panicking, but just because people can't agree on
That's a whole lot of points to argue. You can argue the importance of them, but they're all present in the proposal here. There's also an implicit assumption that people who hang out on RFC threads on GitHub are representative as well. I will personally ignore these if they landed but that's because I just don't unwrap like this. Anyhow and ok_or_else solve the problem quite nicely and don't require a big refactor/cleanup later and the code can move from some form of prototype toy to some form of production by just not panicking way up at the top in main. If it was my job to make this decision I'd honestly start at requiring you to argue me into why we should encourage this kind of code. |
Agree with you. Is there a standard protocol to close RFC or a way to deem a particular RFC is not worth pursuing? OP could have made his suggestions into a crate and use the empirical evidences (if any) to back up his claim. |
In my experience of writing academic scientific software, particularly during early development, panicing by unwrap()/expect() in situations where we don't really know how to recover, leads to better quality code than using If a program panics before finishing, users assume it "did not work", they ignore any partially produced outputs, and they investigate why the panic occurred. This is good, because it means we aren't trusting the possibly incorrect output. While it's as easy to write '?' and pass the problem up, it's hard to test and get it right. I've seen programs which, (for example), when a hard drive gets full output only partial output but appear to finish successfuly. Or, programs which try, and fail, to recover correctly from unusual error situations, like poisoned mutexes. If I'm not carefully testing a particular error state, I find it better to 'unwrap' and explode, than believe I'm handling it with ?, but not really bother testing I'm handling it correctly (and it's hard to test things like poisoned mutexes). Of course, when you are working on a battle-hardened, polished, library intended for use by many people, you add enough tests you can be sure it never panics, and also behaves correctly, but that's a huge amount of work! |
Not every single reason, just to align it with the std macros. We have |
Yes, then how do we decide which are the methods that we ought to add and which we should not add? And what about maintainability? Let's not add too much burden onto the Rust team on maintaining these APIs of such common types, the standard library should be lean and absolutely concise. You have not convinced me that because we have these macros, hence, we should have those corresponding methods in |
@jymchng Regarding developmental and maintenance burdens, we are discussing whether adding |
Rust is a language of longevity. It is precisely this mentality that got C++ to become this bloated with almost a 100 keywords and thousands of public APIs to maintain. Today, you are thinking that it is only an additional of two one-liner methods - others would think the same, and in no time, the standard library would be bloated with hundreds of similar APIs. |
As a libs-api member, I'm not worried about the maintenance burden here. It's definitively a non-issue. Type theoretic concerns are also not going to be factor. (And |
Thirty years ago a C++ maintainer probably thought the same as you did now and it is those who are maintaining it now suffering. The standard library should be very concise and small and the maintainers, in my humble opinion, should be hyper selective and most importantly, prudent, on what gets into the standard library. |
That is the point of an RFC! Nobody is proposing a
I am not the arbiter on what methods should be added to the language. If I were to propose more things, they would go through the same process. I don't think it's fair to criticise this RFC based on things it isn't even proposing! |
If you use anyhow (for example) and panic in main instead of panicking in the rest of your code, you still get the user-drawing panic and you still get the backtrace if you enable that feature (maybe it's defaulted now that stdlib has backtrace support). In Rust you either ignore compiler warnings, explicitly write an ignore, panic at the location, or pass it to the caller. We can ignore the first 2 in the sense that anyone doing them is outside either approach; since errors can't be swallowed away, someone always makes the panic/not-panic decision and you don't get things like truncated output that claim success unless there is code explicitly ignoring the failure. I'm not here to argue whether or not panic versus not-panic in prototypes is better if only because I can play devil's advocate and find enough reasons to not take that stance. I don't like those reasons, but that's not the same as them being bad. But since "just don't panic" is a reasonable stance, that's a subset of users who won't be using this. I am only pointing out that it's one of the implicit arguments being made. I'm not thinking about maintainability because indeed that's a nonissue. I am thinking about whether or not this brings the ecosystem together. That's what I don't see in it. |
there're two branches in
|
@liigo However poorly chosen an existing name is, it is very unlikely that a simple renaming of an existing method will be accepted for the standard library. This RFC is about adding a new method, albeit it being equivalent to an existing method. |
In my code, I've added some "extension functions" like this: use regex::Regex;
use std::sync::LockResult;
#[easy_ext::ext(LockResultExt)]
pub impl<T> LockResult<T> {
fn poison(self) -> T {
self.expect("LockResult is poisoned")
}
}
#[easy_ext::ext(RegexExt)]
pub impl Result<Regex, regex::Error> {
fn re(self) -> Regex {
self.expect("Bad RegEx")
}
} Usage:
|
I just looked at this again, and I realized |
I just came across the blog post Rust’s design goals should be about code. I think however it's actually implemented ("what name"), this RFC fits into the goal of "Clarity of purpose", i.e., making clear that this |
This a very good point, similar to my comment about the proposed Expanding the method name to add clarity, such as using I've felt ambivalent about |
I'm not sure if A really big problem with If you mentally see Whereas if you read This alone I think will be the source of a lot of confusion if they were added like this, It actually took me a minute to realise what you were proposing was using As for handling the negative paths, I struggle to think of a case where someone would want to write |
For For But |
What about |
Between those two, The main problem is the length of it. If this feature is added but it's notably harder to write than just
I'm not sure I agree, but I have used this function in my own code for quite some time now, so I'm definitely used to it. I read I don't think |
|
It would be great to have something like this. I often find myself thinking, "Why isn't there a tool, like |
What's wrong with Option::unwrap_or_else? todo! is a macro, so the signature wouldn't even be clear. Should it be &str or format_args!, latter wouldn't even be more ergonomic. There is even a clippy lint for expect(format!), but I guess former can be inefficient, since it should be only used or todos. What about Option::unimplemented, which I guess would have to be efficient, since it would be used in application code? |
As a not sophisticated Rust user (I follow many of the weird theoretical discussions about the language, but I rarely engage, as my real use cases are pretty simple) I'm sympathetic to the goal of being able to write very clear and concise code (where the intent is always "self-evident", or almost). I understand that But In short, I support the idea of continuing the exploration for better names, even if this leads to a small amount of redundancy on the std lib side, but I also think that RFCs like this one (which one should expect to be at least slightly controversial) should have some extra work behind before being pushed:
|
It may be nice if you could shove a bug number or arbitrary string in the arguments. |
Rephrasing @leb-kuchen's comment, |
The link in the original post to the crate is incorrect. It should be https://github.com/zkrising/unwrap_todo instead of https://github.com/zkrising/unwrap-todo. |
This RFC proposes
Result::todo
andOption::todo
functions which work like.unwrap()
but imply that error handling should be implemented later.As an example:
This is available as a crate, in case you want to use it right now. I've been using this for quite a while now.
n.b. The initial version of this RFC also proposed
.unreachable()
. Upon more thought and some feedback I've decided that.unreachable()
isn't ideal -- It is easily emulated with.expect("reason for why error cannot happen")
. Attaching.unreachable()
onto this RFC drags it down quite a bit. I think.todo()
is a strong improvement to Rust, but I can't think a strong case for.unreachable()
.Rendered