-
-
Notifications
You must be signed in to change notification settings - Fork 3.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
Remove single
and single_mut
#14268
Remove single
and single_mut
#14268
Conversation
I don't have a strong opinion on specifics, but since this kind of change was made before I'd like to see something more global in analysis compared to locally swapping back again. For example, whatever the semantics, I'd prefer to see similar APIs (World.resource/get_resource) be similar in API design. It would be awkward to teach that .single was Option but .resource wasn't. |
@ChristopherBiscardi thanks for the link! I'm very open to changing this to only removing |
I agree with the notes on consistency. I'm also coming around on the macro approach (but yes, different PR!): I'd rather this be a Rust feature where we can just As the engine matures, I'm increasingly against panicking APIs, especially in seemingly innocuous cases like this. While it's nice for prototyping, it's a serious hazard for refactors and production-grade apps. As I see it there are three reasonable options:
My preferences are 3 > 2 > 1, with aggressive documentation cross-linking the macros and the methods and explaining exactly what the macro is sugar for. There are several areas that should use a unified approach here:
We should decide on what to do for all of these areas at the same time and make a consistent decision across the board, pulling in both SME-ECS and @cart. I think they're better implemented as separate PRs to avoid extreme size, but they should be shipped in the same cycle if we make the change. |
@alice-i-cecile should I close this PR and start an issue for that? |
Keep this PR open, but make an issue please :) |
Actually, there's a fourth option: a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unwrapping stuff is easy, catching unwinds is not. I strongly feel that panics are only acceptable in two contexts:
- When invariant are violating going into an
unsafe
block. - When the end application author wants to panic.
Safe library code should never panic, and should instead return an Option
or Result
, or otherwise disable itself and log an error. This applies to both core and ecosystem crates, but not our examples or tests (obviously).
I like this in as far as it promotes a non-panicing api, but the get_single/single
split seems backwards compared to the existing methods.
Could you elaborate on what you mean by this? |
Mostly our api uses |
@NthTensor ah gotcha. I've changed the PR so it only removes |
This isn't true for the standard library: The |
While panicking is obviously unwanted, I feel like this focuses on the wrong issue as just changing And of course after all this the game state might be in an even more unexpected state since the current system didn't run, which will likely will to even more unexpected situations. A reasonable thing to do might be showing an error screen and returning to an earlier "safe" state (e.g. the title screen), but that's very tiresome to do from every single system that could possibly fail. Instead it would be nice if the scheduler could help with that, though I can't see a practical way right now.
The scheduler already catches unwinds (except on WASM I guess), though it doesn't do anything with them other than resuming the unwind in the main thread. |
This sounds interesting. I do kind of feel like the impulse to panic stems from the scheduler's lack of support for monadic error handling. Perhaps, when it stabilizes, we could simply adjust the system trait to allow for functions that return impl Try and pass off residuals to observers? Anyway, very off topic. |
Either way, I think we should still introduce a fallible version of |
If we want to remove these APIs, please consider marking these deprecated and removed in 0.16. Otherwise, it would be massive breaking and harder to migrate, especially for people who rely on the main branch, they will have to patch all the deps. |
@notmd that's a fair point. I'll change my PR tomorrow to leave them in and deprecate them. |
This exists, in the form of |
Closing for now as the discussion on #14275 points towards a more coordinated effort. |
Objective
In spirit of #12660
I believe it is not controversial to say that when an API offers two similar functions with similar names, the shorter will seem like the default. As such, I believe many people will instinctively gravitate to
single
andsingle_mut
overget_single
andget_single_mut
. This means we are subtly pushing users to prefer the implicitly panicking version over the fallible one. This is bad, as it leads to games that may run well on the developers machine but then panic in an edge case.It is currently understood in the wider Rust community that panics should be an exceptional case, a nuclear option for when recovery is impossible.
We should do our best to make it as easy to do the right thing by default (See Falling Into The Pit of Success). This means that it should be easier to handle an error by propagating it with
?
to a logger or similar than to panic. Our current API does the opposite.Solution
single
andsingle_mut
Alternatives
Per the discussion below, renaming
get_single
tosingle
is seen as a bad move for naming consistency reasons.Future Work
I'd like to increase the ergonomics of
single
andsingle_mut
by introducing the following macro, adapted from https://github.com/tbillington/bevy_best_practices?tab=readme-ov-file#getter-macrosWhich allows doing the following:
I'll leave that change to another PR however, as that is more controversial.
Migration Guide
single()
andsingle_mut()
withsingle().unwrap()
andsingle_mut().unwrap()
get_single()
andget_single_mut()
tosingle()
andsingle_mut()