Skip to content

Make Query::single (and friends) return a Result#18082

Merged
alice-i-cecile merged 13 commits intobevyengine:mainfrom
alice-i-cecile:deprecate-single
Mar 2, 2025
Merged

Make Query::single (and friends) return a Result#18082
alice-i-cecile merged 13 commits intobevyengine:mainfrom
alice-i-cecile:deprecate-single

Conversation

@alice-i-cecile
Copy link
Member

@alice-i-cecile alice-i-cecile commented Feb 27, 2025

Objective

As discussed in #14275, Bevy is currently too prone to panic, and makes the easy / beginner-friendly way to do a large number of operations just to panic on failure.

This is seriously frustrating in library code, but also slows down development, as many of the Query::single panics can actually safely be an early return (these panics are often due to a small ordering issue or a change in game state.

More critically, in most "finished" products, panics are unacceptable: any unexpected failures should be handled elsewhere. That's where the new

With the advent of good system error handling, we can now remove this.

Note: I was instrumental in a) introducing this idea in the first place and b) pushing to make the panicking variant the default. The introduction of both let else statements in Rust and the fancy system error handling work in 0.16 have changed my mind on the right balance here.

Solution

  1. Make Query::single and Query::single_mut (and other random related methods) return a Result.
  2. Handle all of Bevy's internal usage of these APIs.
  3. Deprecate Query::get_single and friends, since we've moved their functionality to the nice names.
  4. Add detailed advice on how to best handle these errors.

Generally I like the diff here, although get_single().unwrap() in tests is a bit of a downgrade.

Testing

I've done a global search for .single to track down any missed deprecated usages.

As to whether or not all the migrations were successful, that's what CI is for :)

Future work

Rename Query::get_single and friends to Query::single!

I've opted not to do this in this PR, and smear it across two releases in order to ease the migration. Successive deprecations are much easier to manage than the semantics and types shifting under your feet.

Cart has convinced me to change my mind on this; see #18082 (comment).

Migration guide

Query::single, Query::single_mut and their QueryState equivalents now return a Result. Generally, you'll want to:

  1. Use Bevy 0.16's system error handling to return a Result using the ? operator.
  2. Use a let else Ok(data) block to early return if it's an expected failure.
  3. Use unwrap() or Ok destructuring inside of tests.

The old Query::get_single (etc) methods which did this have been deprecated.

@alice-i-cecile alice-i-cecile added this to the 0.16 milestone Feb 27, 2025
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events X-Needs-SME This type of work requires an SME to approve it. S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Feb 27, 2025
@alice-i-cecile alice-i-cecile requested a review from cart February 27, 2025 23:05
@alice-i-cecile alice-i-cecile added the D-Straightforward Simple bug fixes and API improvements, docs, test and examples label Feb 27, 2025
Copy link
Contributor

@bushrat011899 bushrat011899 left a comment

Choose a reason for hiding this comment

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

Good direction for the engine. I look forward to reclaiming those names so get_single can just be single in 0.17.

Comment on lines -158 to +160
let mut camera_transform = camera_query.single_mut();
let Ok(mut camera_transform) = camera_query.get_single_mut() else {
return;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Could the signature for move_camera not be changed to return Result, allowing the use of ? here too?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but this is an example of a case where silently failing and just returning is always correct. You never want to panic here.

.without::<C>()
.build();
assert_eq!(entity_a, query_a.single(&world));
assert_eq!(entity_a, query_a.get_single(&world).unwrap());
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I prefer to wrap the reference value in Ok(...) rather an unwrap in an assert_eq, as that will print a nicer error message. Has the same end-result of failing the test so it's not that big of a deal though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, mildly agree.

@alice-i-cecile alice-i-cecile requested a review from cart February 28, 2025 00:51
/// fn my_system(query: Query<&A>, mut commands: Commands) {
/// match query.single() {
/// Ok(a) => (), // Do something with `a`
/// Err(err) => match err {
Copy link
Contributor

Choose a reason for hiding this comment

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

I like these docs!

Copy link
Contributor

@Carter0 Carter0 left a comment

Choose a reason for hiding this comment

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

Im a fan!

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Needs-SME This type of work requires an SME to approve it. labels Feb 28, 2025
@alice-i-cecile
Copy link
Member Author

I still need to fix CI, but once that's done I'm going to call this good to merge.

@alice-i-cecile alice-i-cecile changed the title Deprecate Query::single Make Query::single (and friends) return a Result Feb 28, 2025
@IQuick143 IQuick143 added the M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Feb 28, 2025
@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Mar 2, 2025
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Mar 2, 2025
Merged via the queue into bevyengine:main with commit 2ad5908 Mar 2, 2025
31 checks passed
github-merge-queue bot pushed a commit that referenced this pull request Mar 7, 2025
# Objective

Alternative to and closes #18120.

Sibling to #18082, see that PR for broader reasoning.

Folks weren't sold on the name `many` (get_many is clearer, and this is
rare), and that PR is much more complex.

## Solution

- Simply deprecate `Query::many` and `Query::many_mut`
- Clean up internal usages

Mentions of this in the docs can wait until it's fully removed in the
0.17 cycle IMO: it's much easier to catch the problems when doing that.

## Testing

CI!

## Migration Guide

`Query::many` and `Query::many_mut` have been deprecated to reduce
panics and API duplication. Use `Query::get_many` and
`Query::get_many_mut` instead, and handle the `Result`.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events D-Straightforward Simple bug fixes and API improvements, docs, test and examples M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants