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

# 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
https://github.com/bevyengine/bevy/pull/18082#discussion_r1974536085.

## 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.
This commit is contained in:
Alice Cecile 2025-03-02 14:51:56 -05:00 committed by GitHub
parent ff1ae62e1c
commit 2ad5908e58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 263 additions and 270 deletions

View File

@ -162,7 +162,7 @@ fn configure_depth_texture_usages(
}
// Find all the render target that potentially uses OIT
let primary_window = p.get_single().ok();
let primary_window = p.single().ok();
let mut render_target_has_oit = <HashSet<_>>::default();
for (camera, has_oit) in &cameras {
if has_oit {

View File

@ -260,7 +260,7 @@ pub fn debug_draw(
.map(|(entity, camera)| {
(
entity,
camera.target.normalize(primary_window.get_single().ok()),
camera.target.normalize(primary_window.single().ok()),
)
})
.filter_map(|(entity, target)| Some(entity).zip(target))

View File

@ -27,29 +27,29 @@ fn main() {
}
{
let data: &Foo = query.single();
let mut data2: Mut<Foo> = query.single_mut();
let data: &Foo = query.single().unwrap();
let mut data2: Mut<Foo> = query.single_mut().unwrap();
//~^ E0502
assert_eq!(data, &mut *data2); // oops UB
}
{
let mut data2: Mut<Foo> = query.single_mut();
let data: &Foo = query.single();
let mut data2: Mut<Foo> = query.single_mut().unwrap();
let data: &Foo = query.single().unwrap();
//~^ E0502
assert_eq!(data, &mut *data2); // oops UB
}
{
let data: &Foo = query.get_single().unwrap();
let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
let data: &Foo = query.single().unwrap();
let mut data2: Mut<Foo> = query.single_mut().unwrap();
//~^ E0502
assert_eq!(data, &mut *data2); // oops UB
}
{
let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
let data: &Foo = query.get_single().unwrap();
let mut data2: Mut<Foo> = query.single_mut().unwrap();
let data: &Foo = query.single().unwrap();
//~^ E0502
assert_eq!(data, &mut *data2); // oops UB
}

View File

@ -45,9 +45,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable
--> tests/ui/query_lifetime_safety.rs:45:39
|
44 | let data: &Foo = query.get_single().unwrap();
44 | let data: &Foo = query.single().unwrap();
| ----- immutable borrow occurs here
45 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
45 | let mut data2: Mut<Foo> = query.single_mut().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
46 |
47 | assert_eq!(data, &mut *data2); // oops UB
@ -56,9 +56,9 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
error[E0502]: cannot borrow `query` as immutable because it is also borrowed as mutable
--> tests/ui/query_lifetime_safety.rs:52:30
|
51 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
51 | let mut data2: Mut<Foo> = query.single_mut().unwrap();
| ----- mutable borrow occurs here
52 | let data: &Foo = query.get_single().unwrap();
52 | let data: &Foo = query.single().unwrap();
| ^^^^^ immutable borrow occurs here
53 |
54 | assert_eq!(data, &mut *data2); // oops UB

View File

@ -35,13 +35,13 @@ fn for_loops(mut query: Query<&mut Foo>) {
fn single_mut_query(mut query: Query<&mut Foo>) {
// this should fail to compile
{
let mut mut_foo = query.single_mut();
let mut mut_foo = query.single_mut().unwrap();
// This solves "temporary value dropped while borrowed"
let readonly_query = query.as_readonly();
//~^ E0502
let ref_foo = readonly_query.single();
let ref_foo = readonly_query.single().unwrap();
*mut_foo = Foo;
@ -55,7 +55,7 @@ fn single_mut_query(mut query: Query<&mut Foo>) {
let ref_foo = readonly_query.single();
let mut mut_foo = query.single_mut();
let mut mut_foo = query.single_mut().unwrap();
//~^ E0502
println!("{ref_foo:?}");

View File

@ -22,8 +22,8 @@ fn main() {
let mut query_a = lens_a.query();
let mut query_b = lens_b.query();
let a = query_a.single_mut();
let b = query_b.single_mut(); // oops 2 mutable references to same Foo
let a = query_a.single_mut().unwrap();
let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
assert_eq!(*a, *b);
}
@ -34,8 +34,8 @@ fn main() {
let mut query_b = lens.query();
//~^ E0499
let a = query_a.single_mut();
let b = query_b.single_mut(); // oops 2 mutable references to same Foo
let a = query_a.single_mut().unwrap();
let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
assert_eq!(*a, *b);
}
}

View File

@ -1557,7 +1557,7 @@ mod tests {
// Since the world is always ahead, as long as changes can't get older than `u32::MAX` (which we ensure),
// the wrapping difference will always be positive, so wraparound doesn't matter.
let mut query = world.query::<Ref<C>>();
assert!(query.single(&world).is_changed());
assert!(query.single(&world).unwrap().is_changed());
}
#[test]

View File

@ -33,7 +33,7 @@ use super::{FilteredAccess, QueryData, QueryFilter};
/// .build();
///
/// // Consume the QueryState
/// let (entity, b) = query.single(&world);
/// let (entity, b) = query.single(&world).unwrap();
/// ```
pub struct QueryBuilder<'w, D: QueryData = (), F: QueryFilter = ()> {
access: FilteredAccess<ComponentId>,
@ -297,13 +297,13 @@ mod tests {
.with::<A>()
.without::<C>()
.build();
assert_eq!(entity_a, query_a.single(&world));
assert_eq!(entity_a, query_a.single(&world).unwrap());
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
.with::<A>()
.without::<B>()
.build();
assert_eq!(entity_b, query_b.single(&world));
assert_eq!(entity_b, query_b.single(&world).unwrap());
}
#[test]
@ -319,13 +319,13 @@ mod tests {
.with_id(component_id_a)
.without_id(component_id_c)
.build();
assert_eq!(entity_a, query_a.single(&world));
assert_eq!(entity_a, query_a.single(&world).unwrap());
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
.with_id(component_id_a)
.without_id(component_id_b)
.build();
assert_eq!(entity_b, query_b.single(&world));
assert_eq!(entity_b, query_b.single(&world).unwrap());
}
#[test]
@ -385,7 +385,7 @@ mod tests {
.data::<&B>()
.build();
let entity_ref = query.single(&world);
let entity_ref = query.single(&world).unwrap();
assert_eq!(entity, entity_ref.id());
@ -408,7 +408,7 @@ mod tests {
.ref_id(component_id_b)
.build();
let entity_ref = query.single(&world);
let entity_ref = query.single(&world).unwrap();
assert_eq!(entity, entity_ref.id());

View File

@ -104,7 +104,7 @@ impl<'w> PartialEq for QueryEntityError<'w> {
impl<'w> Eq for QueryEntityError<'w> {}
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState) as a single expected result via
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
/// [`single`](crate::system::Query::single) or [`single_mut`](crate::system::Query::single_mut).
#[derive(Debug, Error)]
pub enum QuerySingleError {
/// No entity fits the query.

View File

@ -763,8 +763,8 @@ mod tests {
let _: Option<&Foo> = q.get(&world, e).ok();
let _: Option<&Foo> = q.get_manual(&world, e).ok();
let _: Option<[&Foo; 1]> = q.get_many(&world, [e]).ok();
let _: Option<&Foo> = q.get_single(&world).ok();
let _: &Foo = q.single(&world);
let _: Option<&Foo> = q.single(&world).ok();
let _: &Foo = q.single(&world).unwrap();
// system param
let mut q = SystemState::<Query<&mut Foo>>::new(&mut world);
@ -776,9 +776,9 @@ mod tests {
let _: Option<&Foo> = q.get(e).ok();
let _: Option<[&Foo; 1]> = q.get_many([e]).ok();
let _: Option<&Foo> = q.get_single().ok();
let _: Option<&Foo> = q.single().ok();
let _: [&Foo; 1] = q.many([e]);
let _: &Foo = q.single();
let _: &Foo = q.single().unwrap();
}
// regression test for https://github.com/bevyengine/bevy/pull/8029

View File

@ -1597,43 +1597,88 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// This can only be called for read-only queries,
/// see [`single_mut`](Self::single_mut) for write-queries.
///
/// # Panics
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
/// instead.
///
/// Panics if the number of query results is not exactly one. Use
/// [`get_single`](Self::get_single) to return a `Result` instead of panicking.
#[track_caller]
/// # Example
///
/// Sometimes, you might want to handle the error in a specific way,
/// generally by spawning the missing entity.
///
/// ```rust
/// use bevy_ecs::prelude::*;
/// use bevy_ecs::query::QuerySingleError;
///
/// #[derive(Component)]
/// struct A(usize);
///
/// fn my_system(query: Query<&A>, mut commands: Commands) {
/// match query.single() {
/// Ok(a) => (), // Do something with `a`
/// Err(err) => match err {
/// QuerySingleError::NoEntities(_) => {
/// commands.spawn(A(0));
/// }
/// QuerySingleError::MultipleEntities(_) => panic!("Multiple entities found!"),
/// },
/// }
/// }
/// ```
///
/// However in most cases, this error can simply be handled with a graceful early return.
/// If this is an expected failure mode, you can do this using the `let else` pattern like so:
/// ```rust
/// use bevy_ecs::prelude::*;
///
/// #[derive(Component)]
/// struct A(usize);
///
/// fn my_system(query: Query<&A>) {
/// let Ok(a) = query.single() else {
/// return;
/// };
///
/// // Do something with `a`
/// }
/// ```
///
/// If this is unexpected though, you should probably use the `?` operator
/// in combination with Bevy's error handling apparatus.
///
/// ```rust
/// use bevy_ecs::prelude::*;
///
/// #[derive(Component)]
/// struct A(usize);
///
/// fn my_system(query: Query<&A>) -> Result {
/// let a = query.single()?;
///
/// // Do something with `a`
/// Ok(())
/// }
/// ```
///
/// This allows you to globally control how errors are handled in your application,
/// by setting up a custom error handler.
/// See the [`bevy_ecs::result`] module docs for more information!
/// Commonly, you might want to panic on an error during development, but log the error and continue
/// execution in production.
///
/// Simply unwrapping the [`Result`] also works, but should generally be reserved for tests.
#[inline]
pub fn single<'w>(&mut self, world: &'w World) -> ROQueryItem<'w, D> {
pub fn single<'w>(&mut self, world: &'w World) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
self.query(world).single_inner()
}
/// Returns a single immutable query result when there is exactly one entity matching
/// the query.
///
/// This can only be called for read-only queries,
/// see [`get_single_mut`](Self::get_single_mut) for write-queries.
///
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
/// instead.
/// A deprecated alias for [`QueryState::single`].
#[deprecated(since = "0.16.0", note = "Please use `single` instead.")]
#[inline]
pub fn get_single<'w>(
&mut self,
world: &'w World,
) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
self.query(world).get_single_inner()
}
/// Returns a single mutable query result when there is exactly one entity matching
/// the query.
///
/// # Panics
///
/// Panics if the number of query results is not exactly one. Use
/// [`get_single_mut`](Self::get_single_mut) to return a `Result` instead of panicking.
#[track_caller]
#[inline]
pub fn single_mut<'w>(&mut self, world: &'w mut World) -> D::Item<'w> {
self.query_mut(world).single_inner()
self.single(world)
}
/// Returns a single mutable query result when there is exactly one entity matching
@ -1641,12 +1686,25 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
/// instead.
///
/// # Examples
///
/// Please see [`Query::single`] for advice on handling the error.
#[inline]
pub fn single_mut<'w>(
&mut self,
world: &'w mut World,
) -> Result<D::Item<'w>, QuerySingleError> {
self.query_mut(world).single_inner()
}
/// A deprecated alias for [`QueryState::single_mut`].
#[deprecated(since = "0.16.0", note = "Please use `single` instead.")]
pub fn get_single_mut<'w>(
&mut self,
world: &'w mut World,
) -> Result<D::Item<'w>, QuerySingleError> {
self.query_mut(world).get_single_inner()
self.single_mut(world)
}
/// Returns a query result when there is exactly one entity matching the query.
@ -1659,11 +1717,11 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
#[inline]
pub unsafe fn get_single_unchecked<'w>(
pub unsafe fn single_unchecked<'w>(
&mut self,
world: UnsafeWorldCell<'w>,
) -> Result<D::Item<'w>, QuerySingleError> {
self.query_unchecked(world).get_single_inner()
self.query_unchecked(world).single_inner()
}
/// Returns a query result when there is exactly one entity matching the query,
@ -1679,7 +1737,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
/// with a mismatched [`WorldId`] is unsound.
#[inline]
pub unsafe fn get_single_unchecked_manual<'w>(
pub unsafe fn single_unchecked_manual<'w>(
&self,
world: UnsafeWorldCell<'w>,
last_run: Tick,
@ -1689,7 +1747,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
// - The caller ensured we have the correct access to the world.
// - The caller ensured that the world matches.
self.query_unchecked_manual_with_ticks(world, last_run, this_run)
.get_single_inner()
.single_inner()
}
}
@ -1753,7 +1811,7 @@ mod tests {
let query_state = world.query::<(&A, &B)>();
let mut new_query_state = query_state.transmute::<&A>(&world);
assert_eq!(new_query_state.iter(&world).len(), 1);
let a = new_query_state.single(&world);
let a = new_query_state.single(&world).unwrap();
assert_eq!(a.0, 1);
}
@ -1767,7 +1825,7 @@ mod tests {
let query_state = world.query_filtered::<(&A, &B), Without<C>>();
let mut new_query_state = query_state.transmute::<&A>(&world);
// even though we change the query to not have Without<C>, we do not get the component with C.
let a = new_query_state.single(&world);
let a = new_query_state.single(&world).unwrap();
assert_eq!(a.0, 0);
}
@ -1780,7 +1838,7 @@ mod tests {
let q = world.query::<()>();
let mut q = q.transmute::<Entity>(&world);
assert_eq!(q.single(&world), entity);
assert_eq!(q.single(&world).unwrap(), entity);
}
#[test]
@ -1790,7 +1848,7 @@ mod tests {
let q = world.query::<&A>();
let mut new_q = q.transmute::<Ref<A>>(&world);
assert!(new_q.single(&world).is_added());
assert!(new_q.single(&world).unwrap().is_added());
let q = world.query::<Ref<A>>();
let _ = q.transmute::<&A>(&world);
@ -1861,7 +1919,7 @@ mod tests {
let query_state = world.query::<Option<&A>>();
let mut new_query_state = query_state.transmute::<&A>(&world);
let x = new_query_state.single(&world);
let x = new_query_state.single(&world).unwrap();
assert_eq!(x.0, 1234);
}
@ -1886,7 +1944,7 @@ mod tests {
let mut query = query;
// Our result is completely untyped
let entity_ref = query.single(&world);
let entity_ref = query.single(&world).unwrap();
assert_eq!(entity, entity_ref.id());
assert_eq!(0, entity_ref.get::<A>().unwrap().0);
@ -1901,16 +1959,16 @@ mod tests {
let mut query = QueryState::<(Entity, &A, Has<B>)>::new(&mut world)
.transmute_filtered::<(Entity, Has<B>), Added<A>>(&world);
assert_eq!((entity_a, false), query.single(&world));
assert_eq!((entity_a, false), query.single(&world).unwrap());
world.clear_trackers();
let entity_b = world.spawn((A(0), B(0))).id();
assert_eq!((entity_b, true), query.single(&world));
assert_eq!((entity_b, true), query.single(&world).unwrap());
world.clear_trackers();
assert!(query.get_single(&world).is_err());
assert!(query.single(&world).is_err());
}
#[test]
@ -1922,15 +1980,15 @@ mod tests {
.transmute_filtered::<Entity, Changed<A>>(&world);
let mut change_query = QueryState::<&mut A>::new(&mut world);
assert_eq!(entity_a, detection_query.single(&world));
assert_eq!(entity_a, detection_query.single(&world).unwrap());
world.clear_trackers();
assert!(detection_query.get_single(&world).is_err());
assert!(detection_query.single(&world).is_err());
change_query.single_mut(&mut world).0 = 1;
change_query.single_mut(&mut world).unwrap().0 = 1;
assert_eq!(entity_a, detection_query.single(&world));
assert_eq!(entity_a, detection_query.single(&world).unwrap());
}
#[test]
@ -2017,7 +2075,7 @@ mod tests {
let query_2 = QueryState::<&B, Without<C>>::new(&mut world);
let mut new_query: QueryState<Entity, ()> = query_1.join_filtered(&world, &query_2);
assert_eq!(new_query.single(&world), entity_ab);
assert_eq!(new_query.single(&world).unwrap(), entity_ab);
}
#[test]

View File

@ -270,7 +270,7 @@ use core::{
/// |[`iter_combinations`]\[[`_mut`][`iter_combinations_mut`]]|Returns an iterator over all combinations of a specified number of query items.|
/// |[`get`]\[[`_mut`][`get_mut`]]|Returns the query item for the specified entity.|
/// |[`many`]\[[`_mut`][`many_mut`]],<br>[`get_many`]\[[`_mut`][`get_many_mut`]]|Returns the query items for the specified entities.|
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`get_single`]\[[`_mut`][`get_single_mut`]]|Returns the query item while verifying that there aren't others.|
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`single`]\[[`_mut`][`single_mut`]]|Returns the query item while verifying that there aren't others.|
///
/// There are two methods for each type of query operation: immutable and mutable (ending with `_mut`).
/// When using immutable methods, the query items returned are of type [`ROQueryItem`], a read-only version of the query item.
@ -307,7 +307,7 @@ use core::{
/// |[`get`]\[[`_mut`][`get_mut`]]|O(1)|
/// |([`get_`][`get_many`])[`many`]|O(k)|
/// |([`get_`][`get_many_mut`])[`many_mut`]|O(k<sup>2</sup>)|
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`get_single`]\[[`_mut`][`get_single_mut`]]|O(a)|
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`single`]\[[`_mut`][`single_mut`]]|O(a)|
/// |Archetype based filtering ([`With`], [`Without`], [`Or`])|O(a)|
/// |Change detection filtering ([`Added`], [`Changed`])|O(a + n)|
///
@ -351,8 +351,8 @@ use core::{
/// [`get_many`]: Self::get_many
/// [`get_many_mut`]: Self::get_many_mut
/// [`get_mut`]: Self::get_mut
/// [`get_single`]: Self::get_single
/// [`get_single_mut`]: Self::get_single_mut
/// [`single`]: Self::single
/// [`single_mut`]: Self::single_mut
/// [`iter`]: Self::iter
/// [`iter_combinations`]: Self::iter_combinations
/// [`iter_combinations_mut`]: Self::iter_combinations_mut
@ -947,7 +947,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// friends_query: Query<&Friends>,
/// mut counter_query: Query<&mut Counter>,
/// ) {
/// let friends = friends_query.single();
/// let friends = friends_query.single().unwrap();
/// for mut counter in counter_query.iter_many_unique_inner(friends) {
/// println!("Friend's counter: {:?}", counter.value);
/// counter.value += 1;
@ -1706,36 +1706,6 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
unsafe { self.reborrow_unsafe() }.get_inner(entity)
}
/// Returns a single read-only query item when there is exactly one entity matching the query.
///
/// # Panics
///
/// This method panics if the number of query items is **not** exactly one.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Component)]
/// # struct Player;
/// # #[derive(Component)]
/// # struct Position(f32, f32);
/// fn player_system(query: Query<&Position, With<Player>>) {
/// let player_position = query.single();
/// // do something with player_position
/// }
/// # bevy_ecs::system::assert_is_system(player_system);
/// ```
///
/// # See also
///
/// - [`get_single`](Self::get_single) for the non-panicking version.
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
#[track_caller]
pub fn single(&self) -> ROQueryItem<'_, D> {
self.get_single().unwrap()
}
/// Returns a single read-only query item when there is exactly one entity matching the query.
///
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
@ -1748,7 +1718,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// # #[derive(Component)]
/// # struct PlayerScore(i32);
/// fn player_scoring_system(query: Query<&PlayerScore>) {
/// match query.get_single() {
/// match query.single() {
/// Ok(PlayerScore(score)) => {
/// println!("Score: {}", score);
/// }
@ -1765,43 +1735,16 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// # See also
///
/// - [`get_single_mut`](Self::get_single_mut) to get the mutable query item.
/// - [`single`](Self::single) for the panicking version.
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
#[inline]
pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
self.as_readonly().get_single_inner()
pub fn single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
self.as_readonly().single_inner()
}
/// Returns a single query item when there is exactly one entity matching the query.
///
/// # Panics
///
/// This method panics if the number of query items is **not** exactly one.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Player;
/// # #[derive(Component)]
/// # struct Health(u32);
/// #
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
/// let mut health = query.single_mut();
/// health.0 += 1;
/// }
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
/// ```
///
/// # See also
///
/// - [`get_single_mut`](Self::get_single_mut) for the non-panicking version.
/// - [`single`](Self::single) to get the read-only query item.
#[track_caller]
pub fn single_mut(&mut self) -> D::Item<'_> {
self.get_single_mut().unwrap()
/// A deprecated alias for [`single`](Self::single).
#[deprecated(note = "Please use `single` instead")]
pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
self.single()
}
/// Returns a single query item when there is exactly one entity matching the query.
@ -1819,7 +1762,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// # struct Health(u32);
/// #
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
/// let mut health = query.get_single_mut().expect("Error: Could not find a single player.");
/// let mut health = query.single_mut().expect("Error: Could not find a single player.");
/// health.0 += 1;
/// }
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
@ -1827,19 +1770,22 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// # See also
///
/// - [`get_single`](Self::get_single) to get the read-only query item.
/// - [`single_mut`](Self::single_mut) for the panicking version.
/// - [`single`](Self::single) to get the read-only query item.
#[inline]
pub fn single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
self.reborrow().single_inner()
}
/// A deprecated alias for [`single_mut`](Self::single_mut).
#[deprecated(note = "Please use `single_mut` instead")]
pub fn get_single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
self.reborrow().get_single_inner()
self.single_mut()
}
/// Returns a single query item when there is exactly one entity matching the query.
/// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
///
/// # Panics
///
/// This method panics if the number of query items is **not** exactly one.
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
///
/// # Example
///
@ -1852,7 +1798,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// # struct Health(u32);
/// #
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) {
/// let mut health = query.single_inner();
/// let mut health = query.single_inner().expect("Error: Could not find a single player.");
/// health.0 += 1;
/// }
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
@ -1860,43 +1806,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// # See also
///
/// - [`get_single_inner`](Self::get_single_inner) for the non-panicking version.
/// - [`single`](Self::single) to get the read-only query item.
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
#[track_caller]
pub fn single_inner(self) -> D::Item<'w> {
self.get_single_inner().unwrap()
}
/// Returns a single query item when there is exactly one entity matching the query.
/// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
///
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Player;
/// # #[derive(Component)]
/// # struct Health(u32);
/// #
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) {
/// let mut health = query.get_single_inner().expect("Error: Could not find a single player.");
/// health.0 += 1;
/// }
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
/// ```
///
/// # See also
///
/// - [`get_single`](Self::get_single) to get the read-only query item.
/// - [`get_single_mut`](Self::get_single_mut) to get the mutable query item.
/// - [`single_inner`](Self::single_inner) for the panicking version.
#[inline]
pub fn get_single_inner(self) -> Result<D::Item<'w>, QuerySingleError> {
pub fn single_inner(self) -> Result<D::Item<'w>, QuerySingleError> {
let mut query = self.into_iter();
let first = query.next();
let extra = query.next().is_some();
@ -2001,7 +1915,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// # world.spawn((A(10), B(5)));
/// #
/// fn reusable_function(lens: &mut QueryLens<&A>) {
/// assert_eq!(lens.query().single().0, 10);
/// assert_eq!(lens.query().single().unwrap().0, 10);
/// }
///
/// // We can use the function in a system that takes the exact query.
@ -2160,7 +2074,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// # world.spawn((A(10), B(5)));
/// #
/// fn reusable_function(mut lens: QueryLens<&A>) {
/// assert_eq!(lens.query().single().0, 10);
/// assert_eq!(lens.query().single().unwrap().0, 10);
/// }
///
/// // We can use the function in a system that takes the exact query.

View File

@ -408,7 +408,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
};
let single = query
.get_single_inner()
.single_inner()
.expect("The query was expected to contain exactly one matching entity.");
Single {
item: single,
@ -432,7 +432,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
world.change_tick(),
)
};
let is_valid = query.get_single_inner().is_ok();
let is_valid = query.single_inner().is_ok();
if !is_valid {
system_meta.try_warn_param::<Self>();
}
@ -474,7 +474,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
let query = unsafe {
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
};
match query.get_single_inner() {
match query.single_inner() {
Ok(single) => Some(Single {
item: single,
_filter: PhantomData,
@ -500,7 +500,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
world.change_tick(),
)
};
let result = query.get_single_inner();
let result = query.single_inner();
let is_valid = !matches!(result, Err(QuerySingleError::MultipleEntities(_)));
if !is_valid {
system_meta.try_warn_param::<Self>();

View File

@ -2430,11 +2430,11 @@ impl<'w> EntityWorldMut<'w> {
/// let mut entity = world.spawn_empty();
/// entity.entry().or_insert_with(|| Comp(4));
/// # let entity_id = entity.id();
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
///
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
/// assert_eq!(world.query::<&Comp>().single(&world).0, 5);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 5);
/// ```
///
/// # Panics
@ -2714,7 +2714,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> Entry<'w, 'a, T> {
/// let mut entity = world.spawn(Comp(0));
///
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
/// assert_eq!(world.query::<&Comp>().single(&world).0, 1);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 1);
/// ```
#[inline]
pub fn and_modify<F: FnOnce(Mut<'_, T>)>(self, f: F) -> Self {
@ -2773,11 +2773,11 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> {
///
/// entity.entry().or_insert(Comp(4));
/// # let entity_id = entity.id();
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
///
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
/// entity.entry().or_insert(Comp(15)).into_mut().0 *= 2;
/// assert_eq!(world.query::<&Comp>().single(&world).0, 8);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 8);
/// ```
#[inline]
pub fn or_insert(self, default: T) -> OccupiedEntry<'w, 'a, T> {
@ -2801,7 +2801,7 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> {
/// let mut entity = world.spawn_empty();
///
/// entity.entry().or_insert_with(|| Comp(4));
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
/// ```
#[inline]
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> OccupiedEntry<'w, 'a, T> {
@ -2827,7 +2827,7 @@ impl<'w, 'a, T: Component + Default> Entry<'w, 'a, T> {
/// let mut entity = world.spawn_empty();
///
/// entity.entry::<Comp>().or_default();
/// assert_eq!(world.query::<&Comp>().single(&world).0, 0);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 0);
/// ```
#[inline]
pub fn or_default(self) -> OccupiedEntry<'w, 'a, T> {
@ -2885,7 +2885,7 @@ impl<'w, 'a, T: Component> OccupiedEntry<'w, 'a, T> {
/// o.insert(Comp(10));
/// }
///
/// assert_eq!(world.query::<&Comp>().single(&world).0, 10);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 10);
/// ```
#[inline]
pub fn insert(&mut self, component: T) {
@ -2943,7 +2943,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> OccupiedEntry<'w, 'a, T> {
/// o.get_mut().0 += 2
/// }
///
/// assert_eq!(world.query::<&Comp>().single(&world).0, 17);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 17);
/// ```
#[inline]
pub fn get_mut(&mut self) -> Mut<'_, T> {
@ -2972,7 +2972,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> OccupiedEntry<'w, 'a, T> {
/// o.into_mut().0 += 10;
/// }
///
/// assert_eq!(world.query::<&Comp>().single(&world).0, 15);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 15);
/// ```
#[inline]
pub fn into_mut(self) -> Mut<'a, T> {
@ -3004,7 +3004,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
/// v.insert(Comp(10));
/// }
///
/// assert_eq!(world.query::<&Comp>().single(&world).0, 10);
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 10);
/// ```
#[inline]
pub fn insert(self, component: T) -> OccupiedEntry<'w, 'a, T> {
@ -3036,7 +3036,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
/// .data::<&A>()
/// .build();
///
/// let filtered_entity: FilteredEntityRef = query.single(&mut world);
/// let filtered_entity: FilteredEntityRef = query.single(&mut world).unwrap();
/// let component: &A = filtered_entity.get().unwrap();
///
/// // Here `FilteredEntityRef` is nested in a tuple, so it does not have access to `&A`.
@ -3044,7 +3044,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
/// .data::<&A>()
/// .build();
///
/// let (_, filtered_entity) = query.single(&mut world);
/// let (_, filtered_entity) = query.single(&mut world).unwrap();
/// assert!(filtered_entity.get::<A>().is_none());
/// ```
#[derive(Clone)]
@ -3367,7 +3367,7 @@ unsafe impl TrustedEntityBorrow for FilteredEntityRef<'_> {}
/// .data::<&mut A>()
/// .build();
///
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world);
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world).unwrap();
/// let component: Mut<A> = filtered_entity.get_mut().unwrap();
///
/// // Here `FilteredEntityMut` is nested in a tuple, so it does not have access to `&mut A`.
@ -3375,7 +3375,7 @@ unsafe impl TrustedEntityBorrow for FilteredEntityRef<'_> {}
/// .data::<&mut A>()
/// .build();
///
/// let (_, mut filtered_entity) = query.single_mut(&mut world);
/// let (_, mut filtered_entity) = query.single_mut(&mut world).unwrap();
/// assert!(filtered_entity.get_mut::<A>().is_none());
/// ```
pub struct FilteredEntityMut<'w> {

View File

@ -226,7 +226,7 @@ pub fn dispatch_focused_input<E: Event + Clone>(
windows: Query<Entity, With<PrimaryWindow>>,
mut commands: Commands,
) {
if let Ok(window) = windows.get_single() {
if let Ok(window) = windows.single() {
// If an element has keyboard focus, then dispatch the input event to that element.
if let Some(focused_entity) = focus.0 {
for ev in key_events.read() {

View File

@ -251,7 +251,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
fallback_image,
)
} else {
RenderViewIrradianceVolumeBindGroupEntries::get_single(
RenderViewIrradianceVolumeBindGroupEntries::single(
render_view_irradiance_volumes,
images,
fallback_image,
@ -295,7 +295,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
/// Looks up and returns the bindings for any irradiance volumes visible in
/// the view, as well as the sampler. This is the version used when binding
/// arrays aren't available on the current platform.
fn get_single(
fn single(
render_view_irradiance_volumes: Option<&RenderViewLightProbes<IrradianceVolume>>,
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,

View File

@ -118,7 +118,7 @@ pub fn mouse_pick_events(
WindowEvent::CursorMoved(event) => {
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(event.window))
.normalize(primary_window.get_single().ok())
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,
@ -138,7 +138,7 @@ pub fn mouse_pick_events(
WindowEvent::MouseButtonInput(input) => {
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(input.window))
.normalize(primary_window.get_single().ok())
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,
@ -162,7 +162,7 @@ pub fn mouse_pick_events(
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(window))
.normalize(primary_window.get_single().ok())
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,
@ -195,7 +195,7 @@ pub fn touch_pick_events(
let pointer = PointerId::Touch(touch.id);
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(touch.window))
.normalize(primary_window.get_single().ok())
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,

View File

@ -224,7 +224,7 @@ impl Location {
) -> bool {
if camera
.target
.normalize(Some(match primary_window.get_single() {
.normalize(Some(match primary_window.single() {
Ok(w) => w,
Err(_) => return false,
}))

View File

@ -317,7 +317,7 @@ impl Plugin for RenderPlugin {
let primary_window = app
.world_mut()
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
.get_single(app.world())
.single(app.world())
.ok()
.cloned();
let settings = render_creation.clone();

View File

@ -539,7 +539,7 @@ mod tests {
// Only one synchronized entity
assert!(q.iter(&render_world).count() == 1);
let render_entity = q.get_single(&render_world).unwrap();
let render_entity = q.single(&render_world).unwrap();
let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
assert!(render_entity_component.id() == render_entity);

View File

@ -588,7 +588,8 @@ mod tests {
let (scene_entity, scene_component_a) = app
.world_mut()
.query::<(Entity, &ComponentA)>()
.single(app.world());
.single(app.world())
.unwrap();
assert_eq!(scene_component_a.x, 3.0);
assert_eq!(scene_component_a.y, 4.0);
assert_eq!(
@ -631,7 +632,10 @@ mod tests {
// clone only existing entity
let mut scene_spawner = SceneSpawner::default();
let entity = world.query_filtered::<Entity, With<A>>().single(&world);
let entity = world
.query_filtered::<Entity, With<A>>()
.single(&world)
.unwrap();
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entity(entity)
.build();

View File

@ -763,12 +763,12 @@ mod tests {
let bar_to_foo = dst_world
.query_filtered::<&MyEntityRef, Without<Foo>>()
.get_single(&dst_world)
.single(&dst_world)
.cloned()
.unwrap();
let foo = dst_world
.query_filtered::<Entity, With<Foo>>()
.get_single(&dst_world)
.single(&dst_world)
.unwrap();
assert_eq!(foo, bar_to_foo.0);
@ -793,7 +793,7 @@ mod tests {
deserialized_scene
.write_to_world(&mut world, &mut EntityHashMap::default())
.unwrap();
assert_eq!(&qux, world.query::<&Qux>().single(&world));
assert_eq!(&qux, world.query::<&Qux>().single(&world).unwrap());
}
#[test]

View File

@ -115,7 +115,7 @@ fn sprite_picking(
-transform.translation().z
});
let primary_window = primary_window.get_single().ok();
let primary_window = primary_window.single().ok();
for (pointer, location) in pointers.iter().filter_map(|(pointer, pointer_location)| {
pointer_location.location().map(|loc| (pointer, loc))

View File

@ -153,7 +153,7 @@ pub fn extract_text2d_sprite(
) {
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows
.get_single()
.single()
.map(|window| window.resolution.scale_factor())
.unwrap_or(1.0);
let scaling = GlobalTransform::from_scale(Vec2::splat(scale_factor.recip()).extend(1.));
@ -256,7 +256,7 @@ pub fn update_text2d_layout(
) {
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows
.get_single()
.single()
.ok()
.map(|window| window.resolution.scale_factor())
.or(*last_scale_factor)

View File

@ -43,7 +43,7 @@ fn calc_bounds(
Ref<GlobalTransform>,
)>,
) {
if let Ok((camera, camera_transform)) = camera.get_single() {
if let Ok((camera, camera_transform)) = camera.single() {
for (mut accessible, node, transform) in &mut nodes {
if node.is_changed() || transform.is_changed() {
if let Ok(translation) =

View File

@ -726,7 +726,7 @@ mod tests {
mut cameras: Query<&mut Camera>,
) {
let primary_window = primary_window_query
.get_single()
.single()
.expect("missing primary window");
let camera_count = cameras.iter().len();
for (camera_index, mut camera) in cameras.iter_mut().enumerate() {
@ -783,7 +783,7 @@ mod tests {
ui_schedule.run(world);
let (ui_node_entity, UiTargetCamera(target_camera_entity)) = world
.query_filtered::<(Entity, &UiTargetCamera), With<MovingUiNode>>()
.get_single(world)
.single(world)
.expect("missing MovingUiNode");
assert_eq!(expected_camera_entity, target_camera_entity);
let mut ui_surface = world.resource_mut::<UiSurface>();

View File

@ -84,7 +84,7 @@ pub fn ui_picking(
.map(|(entity, camera, _)| {
(
entity,
camera.target.normalize(primary_window.get_single().ok()),
camera.target.normalize(primary_window.single().ok()),
)
})
.filter_map(|(entity, target)| Some(entity).zip(target))

View File

@ -2691,7 +2691,7 @@ pub struct DefaultUiCamera<'w, 's> {
impl<'w, 's> DefaultUiCamera<'w, 's> {
pub fn get(&self) -> Option<Entity> {
self.default_cameras.get_single().ok().or_else(|| {
self.default_cameras.single().ok().or_else(|| {
// If there isn't a single camera and the query isn't empty, there is two or more cameras queried.
if !self.default_cameras.is_empty() {
once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed."));

View File

@ -183,7 +183,7 @@ fn update_accessibility_nodes(
)>,
node_entities: Query<Entity, With<AccessibilityNode>>,
) {
let Ok((primary_window_id, primary_window)) = primary_window.get_single() else {
let Ok((primary_window_id, primary_window)) = primary_window.single() else {
return;
};
let Some(adapter) = adapters.get_mut(&primary_window_id) else {

View File

@ -551,7 +551,7 @@ impl<T: Event> WinitAppRunnerState<T> {
let mut query = self
.world_mut()
.query_filtered::<Entity, With<PrimaryWindow>>();
let entity = query.single(&self.world());
let entity = query.single(&self.world()).unwrap();
self.world_mut()
.entity_mut(entity)
.remove::<RawHandleWrapper>();
@ -571,7 +571,7 @@ impl<T: Event> WinitAppRunnerState<T> {
// handle wrapper removed when the app was suspended.
let mut query = self.world_mut()
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
if let Ok((entity, window)) = query.get_single(&self.world()) {
if let Ok((entity, window)) = query.single(&self.world()) {
let window = window.clone();
let mut create_window =

View File

@ -15,7 +15,7 @@ fn draw_cursor(
window: Query<&Window>,
mut gizmos: Gizmos,
) {
let Ok(window) = window.get_single() else {
let Ok(window) = window.single() else {
return;
};

View File

@ -16,7 +16,7 @@ fn draw_cursor(
windows: Query<&Window>,
mut gizmos: Gizmos,
) {
let Ok(windows) = windows.get_single() else {
let Ok(windows) = windows.single() else {
return;
};

View File

@ -78,7 +78,7 @@ fn input_handler(
time: Res<Time>,
) {
if keyboard_input.just_pressed(KeyCode::Space) {
let mesh_handle = mesh_query.get_single().expect("Query not successful");
let mesh_handle = mesh_query.single().expect("Query not successful");
let mesh = meshes.get_mut(mesh_handle).unwrap();
toggle_texture(mesh);
}

View File

@ -212,7 +212,7 @@ fn drag_drop_image(
mat.base_color_texture = Some(new_image.clone());
// Despawn the image viewer instructions
if let Ok(text_entity) = text.get_single() {
if let Ok(text_entity) = text.single() {
commands.entity(text_entity).despawn();
}
}

View File

@ -258,7 +258,7 @@ fn get_async_loading_state(
// If loaded, change the state.
if is_loaded {
next_loading_state.set(LoadingState::Loaded);
if let Ok(mut text) = text.get_single_mut() {
if let Ok(mut text) = text.single_mut() {
"Loaded!".clone_into(&mut **text);
}
}

View File

@ -35,7 +35,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
struct MyMusic;
fn update_speed(music_controller: Query<&AudioSink, With<MyMusic>>, time: Res<Time>) {
let Ok(sink) = music_controller.get_single() else {
let Ok(sink) = music_controller.single() else {
return;
};
@ -46,7 +46,7 @@ fn pause(
keyboard_input: Res<ButtonInput<KeyCode>>,
music_controller: Query<&AudioSink, With<MyMusic>>,
) {
let Ok(sink) = music_controller.get_single() else {
let Ok(sink) = music_controller.single() else {
return;
};
@ -59,7 +59,7 @@ fn mute(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
) {
let Ok(mut sink) = music_controller.get_single_mut() else {
let Ok(mut sink) = music_controller.single_mut() else {
return;
};
@ -72,7 +72,7 @@ fn volume(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
) {
let Ok(mut sink) = music_controller.get_single_mut() else {
let Ok(mut sink) = music_controller.single_mut() else {
return;
};

View File

@ -165,7 +165,7 @@ fn screen_shake(
}
} else {
// return camera to the latest position of player (it's fixed in this example case)
if let Ok((mut camera, mut transform)) = query.get_single_mut() {
if let Ok((mut camera, mut transform)) = query.single_mut() {
let sub_view = camera.sub_camera_view.as_mut().unwrap();
let target = screen_shake.latest_position.unwrap();
sub_view

View File

@ -69,7 +69,7 @@ fn list_all_named_entities(
text_string.push_str(&format!("{:?}\n", name));
}
if let Ok(mut text) = name_text_query.get_single_mut() {
if let Ok(mut text) = name_text_query.single_mut() {
*text = Text::new(text_string);
} else {
commands.spawn((

View File

@ -175,7 +175,7 @@ fn handle_click(
windows: Query<&Window>,
mut commands: Commands,
) {
let Ok(windows) = windows.get_single() else {
let Ok(windows) = windows.single() else {
return;
};

View File

@ -43,7 +43,7 @@ fn move_system(mut sprites: Query<(&mut Transform, &Velocity)>) {
// Bounce sprites outside the window
fn bounce_system(window: Query<&Window>, mut sprites: Query<(&Transform, &mut Velocity)>) {
let Ok(window) = window.get_single() else {
let Ok(window) = window.single() else {
return;
};
let width = window.width();

View File

@ -256,7 +256,7 @@ fn collisions(
mut query: Query<(&mut Velocity, &mut Transform), With<Contributor>>,
mut rng: ResMut<SharedRng>,
) {
let Ok(window) = window.get_single() else {
let Ok(window) = window.single() else {
return;
};

View File

@ -112,7 +112,7 @@ fn run_camera_controller(
) {
let dt = time.delta_secs();
let Ok((mut transform, mut controller)) = query.get_single_mut() else {
let Ok((mut transform, mut controller)) = query.single_mut() else {
return;
};

View File

@ -61,7 +61,7 @@ fn touch_camera(
mut last_position: Local<Option<Vec2>>,
mut rotations: EventReader<RotationGesture>,
) {
let Ok(window) = window.get_single() else {
let Ok(window) = window.single() else {
return;
};

View File

@ -333,7 +333,7 @@ fn mouse_handler(
mut rng: Local<Option<ChaCha8Rng>>,
mut wave: Local<usize>,
) {
let Ok(window) = window.get_single() else {
let Ok(window) = window.single() else {
return;
};
@ -534,7 +534,7 @@ fn handle_collision(half_extents: Vec2, translation: &Vec3, velocity: &mut Vec3)
}
}
fn collision_system(window: Query<&Window>, mut bird_query: Query<(&mut Bird, &Transform)>) {
let Ok(window) = window.get_single() else {
let Ok(window) = window.single() else {
return;
};

View File

@ -26,7 +26,7 @@ fn setup(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
window: Query<&Window>,
) {
) -> Result {
// circular base
commands.spawn((
Mesh3d(meshes.add(Circle::new(4.0))),
@ -56,7 +56,7 @@ fn setup(
}
// cameras
let window = window.single();
let window = window.single()?;
let width = window.resolution.width() / CAMERA_COLS as f32 * window.resolution.scale_factor();
let height = window.resolution.height() / CAMERA_ROWS as f32 * window.resolution.scale_factor();
let mut i = 0;
@ -83,6 +83,7 @@ fn setup(
i += 1;
}
}
Ok(())
}
fn rotate_cameras(time: Res<Time>, mut query: Query<&mut Transform, With<Camera>>) {

View File

@ -155,7 +155,9 @@ fn setup(mut commands: Commands, font: Res<FontHandle>, args: Res<Args>) {
// System for rotating and translating the camera
fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) {
let mut camera_transform = camera_query.single_mut();
let Ok(mut camera_transform) = camera_query.single_mut() else {
return;
};
camera_transform.rotate_z(time.delta_secs() * 0.5);
*camera_transform =
*camera_transform * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_secs());

View File

@ -188,7 +188,7 @@ fn update_text(
return;
};
let Ok(text) = texts.get_single() else {
let Ok(text) = texts.single() else {
return;
};

View File

@ -92,7 +92,7 @@ fn handle_input(
mut dir: ResMut<ResizeDir>,
example_text: Query<Entity, With<Text>>,
mut writer: TextUiWriter,
) {
) -> Result {
use LeftClickAction::*;
if input.just_pressed(KeyCode::KeyA) {
*action = match *action {
@ -100,7 +100,7 @@ fn handle_input(
Resize => Nothing,
Nothing => Move,
};
*writer.text(example_text.single(), 4) = format!("{:?}", *action);
*writer.text(example_text.single()?, 4) = format!("{:?}", *action);
}
if input.just_pressed(KeyCode::KeyS) {
@ -108,13 +108,15 @@ fn handle_input(
.0
.checked_sub(1)
.unwrap_or(DIRECTIONS.len().saturating_sub(1));
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
}
if input.just_pressed(KeyCode::KeyD) {
dir.0 = (dir.0 + 1) % DIRECTIONS.len();
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
}
Ok(())
}
fn move_or_resize_windows(

View File

@ -39,7 +39,7 @@ fn spawn_player(mut commands: Commands) {
fn spell_casting(mut player: Query<&mut Player>, keyboard_input: Res<ButtonInput<KeyCode>>) {
if keyboard_input.just_pressed(KeyCode::Space) {
let Ok(mut player) = player.get_single_mut() else {
let Ok(mut player) = player.single_mut() else {
return;
};
@ -76,7 +76,7 @@ fn test_player_spawn() {
// Now that the startup systems have run, we can check if the player has
// spawned as expected.
let expected = Player::default();
let actual = app.world_mut().query::<&Player>().get_single(app.world());
let actual = app.world_mut().query::<&Player>().single(app.world());
assert!(actual.is_ok(), "There should be exactly 1 player.");
assert_eq!(
expected.mana,
@ -98,7 +98,11 @@ fn test_spell_casting() {
app.update();
let expected = Player::default();
let actual = app.world_mut().query::<&Player>().single(app.world());
let actual = app
.world_mut()
.query::<&Player>()
.single(app.world())
.unwrap();
assert_eq!(
expected.mana - 1,
actual.mana,
@ -112,7 +116,11 @@ fn test_spell_casting() {
app.update();
// No extra spells have been cast, so no mana should have been used.
let after_keypress_event = app.world_mut().query::<&Player>().single(app.world());
let after_keypress_event = app
.world_mut()
.query::<&Player>()
.single(app.world())
.unwrap();
assert_eq!(
expected.mana - 1,
after_keypress_event.mana,
@ -127,6 +135,10 @@ fn test_window_title() {
app.update();
let window = app.world_mut().query::<&Window>().single(app.world());
let window = app
.world_mut()
.query::<&Window>()
.single(app.world())
.unwrap();
assert_eq!(window.title, "This is window 0!");
}

View File

@ -41,7 +41,7 @@ fn startup(mut cmds: Commands) {
}
fn toggle_window_mode(mut qry_window: Query<&mut Window>) {
let Ok(mut window) = qry_window.get_single_mut() else {
let Ok(mut window) = qry_window.single_mut() else {
return;
};