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 // 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(); let mut render_target_has_oit = <HashSet<_>>::default();
for (camera, has_oit) in &cameras { for (camera, has_oit) in &cameras {
if has_oit { if has_oit {

View File

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

View File

@ -27,29 +27,29 @@ fn main() {
} }
{ {
let data: &Foo = query.single(); let data: &Foo = query.single().unwrap();
let mut data2: Mut<Foo> = query.single_mut(); let mut data2: Mut<Foo> = query.single_mut().unwrap();
//~^ E0502 //~^ E0502
assert_eq!(data, &mut *data2); // oops UB assert_eq!(data, &mut *data2); // oops UB
} }
{ {
let mut data2: Mut<Foo> = query.single_mut(); let mut data2: Mut<Foo> = query.single_mut().unwrap();
let data: &Foo = query.single(); let data: &Foo = query.single().unwrap();
//~^ E0502 //~^ E0502
assert_eq!(data, &mut *data2); // oops UB assert_eq!(data, &mut *data2); // oops UB
} }
{ {
let data: &Foo = query.get_single().unwrap(); let data: &Foo = query.single().unwrap();
let mut data2: Mut<Foo> = query.get_single_mut().unwrap(); let mut data2: Mut<Foo> = query.single_mut().unwrap();
//~^ E0502 //~^ E0502
assert_eq!(data, &mut *data2); // oops UB assert_eq!(data, &mut *data2); // oops UB
} }
{ {
let mut data2: Mut<Foo> = query.get_single_mut().unwrap(); let mut data2: Mut<Foo> = query.single_mut().unwrap();
let data: &Foo = query.get_single().unwrap(); let data: &Foo = query.single().unwrap();
//~^ E0502 //~^ E0502
assert_eq!(data, &mut *data2); // oops UB 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 error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable
--> tests/ui/query_lifetime_safety.rs:45:39 --> 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 | ----- 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 | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
46 | 46 |
47 | assert_eq!(data, &mut *data2); // oops UB 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 error[E0502]: cannot borrow `query` as immutable because it is also borrowed as mutable
--> tests/ui/query_lifetime_safety.rs:52:30 --> 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 | ----- mutable borrow occurs here
52 | let data: &Foo = query.get_single().unwrap(); 52 | let data: &Foo = query.single().unwrap();
| ^^^^^ immutable borrow occurs here | ^^^^^ immutable borrow occurs here
53 | 53 |
54 | assert_eq!(data, &mut *data2); // oops UB 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>) { fn single_mut_query(mut query: Query<&mut Foo>) {
// this should fail to compile // 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" // This solves "temporary value dropped while borrowed"
let readonly_query = query.as_readonly(); let readonly_query = query.as_readonly();
//~^ E0502 //~^ E0502
let ref_foo = readonly_query.single(); let ref_foo = readonly_query.single().unwrap();
*mut_foo = Foo; *mut_foo = Foo;
@ -55,7 +55,7 @@ fn single_mut_query(mut query: Query<&mut Foo>) {
let ref_foo = readonly_query.single(); let ref_foo = readonly_query.single();
let mut mut_foo = query.single_mut(); let mut mut_foo = query.single_mut().unwrap();
//~^ E0502 //~^ E0502
println!("{ref_foo:?}"); println!("{ref_foo:?}");

View File

@ -22,8 +22,8 @@ fn main() {
let mut query_a = lens_a.query(); let mut query_a = lens_a.query();
let mut query_b = lens_b.query(); let mut query_b = lens_b.query();
let a = query_a.single_mut(); let a = query_a.single_mut().unwrap();
let b = query_b.single_mut(); // oops 2 mutable references to same Foo let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
assert_eq!(*a, *b); assert_eq!(*a, *b);
} }
@ -34,8 +34,8 @@ fn main() {
let mut query_b = lens.query(); let mut query_b = lens.query();
//~^ E0499 //~^ E0499
let a = query_a.single_mut(); let a = query_a.single_mut().unwrap();
let b = query_b.single_mut(); // oops 2 mutable references to same Foo let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
assert_eq!(*a, *b); 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), // 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. // the wrapping difference will always be positive, so wraparound doesn't matter.
let mut query = world.query::<Ref<C>>(); let mut query = world.query::<Ref<C>>();
assert!(query.single(&world).is_changed()); assert!(query.single(&world).unwrap().is_changed());
} }
#[test] #[test]

View File

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

View File

@ -104,7 +104,7 @@ impl<'w> PartialEq for QueryEntityError<'w> {
impl<'w> Eq 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 /// 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)] #[derive(Debug, Error)]
pub enum QuerySingleError { pub enum QuerySingleError {
/// No entity fits the query. /// 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(&world, e).ok();
let _: Option<&Foo> = q.get_manual(&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; 1]> = q.get_many(&world, [e]).ok();
let _: Option<&Foo> = q.get_single(&world).ok(); let _: Option<&Foo> = q.single(&world).ok();
let _: &Foo = q.single(&world); let _: &Foo = q.single(&world).unwrap();
// system param // system param
let mut q = SystemState::<Query<&mut Foo>>::new(&mut world); 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> = q.get(e).ok();
let _: Option<[&Foo; 1]> = q.get_many([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; 1] = q.many([e]);
let _: &Foo = q.single(); let _: &Foo = q.single().unwrap();
} }
// regression test for https://github.com/bevyengine/bevy/pull/8029 // 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, /// This can only be called for read-only queries,
/// see [`single_mut`](Self::single_mut) for write-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 /// # Example
/// [`get_single`](Self::get_single) to return a `Result` instead of panicking. ///
#[track_caller] /// 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] #[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() self.query(world).single_inner()
} }
/// Returns a single immutable query result when there is exactly one entity matching /// A deprecated alias for [`QueryState::single`].
/// the query. #[deprecated(since = "0.16.0", note = "Please use `single` instead.")]
///
/// 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.
#[inline] #[inline]
pub fn get_single<'w>( pub fn get_single<'w>(
&mut self, &mut self,
world: &'w World, world: &'w World,
) -> Result<ROQueryItem<'w, D>, QuerySingleError> { ) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
self.query(world).get_single_inner() self.single(world)
}
/// 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()
} }
/// Returns a single mutable query result when there is exactly one entity matching /// 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 /// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
/// instead. /// instead.
///
/// # Examples
///
/// Please see [`Query::single`] for advice on handling the error.
#[inline] #[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>( pub fn get_single_mut<'w>(
&mut self, &mut self,
world: &'w mut World, world: &'w mut World,
) -> Result<D::Item<'w>, QuerySingleError> { ) -> 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. /// 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 /// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query. /// have unique access to the components they query.
#[inline] #[inline]
pub unsafe fn get_single_unchecked<'w>( pub unsafe fn single_unchecked<'w>(
&mut self, &mut self,
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
) -> Result<D::Item<'w>, QuerySingleError> { ) -> 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, /// 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` /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
/// with a mismatched [`WorldId`] is unsound. /// with a mismatched [`WorldId`] is unsound.
#[inline] #[inline]
pub unsafe fn get_single_unchecked_manual<'w>( pub unsafe fn single_unchecked_manual<'w>(
&self, &self,
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
last_run: Tick, 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 we have the correct access to the world.
// - The caller ensured that the world matches. // - The caller ensured that the world matches.
self.query_unchecked_manual_with_ticks(world, last_run, this_run) 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 query_state = world.query::<(&A, &B)>();
let mut new_query_state = query_state.transmute::<&A>(&world); let mut new_query_state = query_state.transmute::<&A>(&world);
assert_eq!(new_query_state.iter(&world).len(), 1); 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); assert_eq!(a.0, 1);
} }
@ -1767,7 +1825,7 @@ mod tests {
let query_state = world.query_filtered::<(&A, &B), Without<C>>(); let query_state = world.query_filtered::<(&A, &B), Without<C>>();
let mut new_query_state = query_state.transmute::<&A>(&world); 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. // 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); assert_eq!(a.0, 0);
} }
@ -1780,7 +1838,7 @@ mod tests {
let q = world.query::<()>(); let q = world.query::<()>();
let mut q = q.transmute::<Entity>(&world); let mut q = q.transmute::<Entity>(&world);
assert_eq!(q.single(&world), entity); assert_eq!(q.single(&world).unwrap(), entity);
} }
#[test] #[test]
@ -1790,7 +1848,7 @@ mod tests {
let q = world.query::<&A>(); let q = world.query::<&A>();
let mut new_q = q.transmute::<Ref<A>>(&world); 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 = world.query::<Ref<A>>();
let _ = q.transmute::<&A>(&world); let _ = q.transmute::<&A>(&world);
@ -1861,7 +1919,7 @@ mod tests {
let query_state = world.query::<Option<&A>>(); let query_state = world.query::<Option<&A>>();
let mut new_query_state = query_state.transmute::<&A>(&world); 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); assert_eq!(x.0, 1234);
} }
@ -1886,7 +1944,7 @@ mod tests {
let mut query = query; let mut query = query;
// Our result is completely untyped // 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!(entity, entity_ref.id());
assert_eq!(0, entity_ref.get::<A>().unwrap().0); 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) let mut query = QueryState::<(Entity, &A, Has<B>)>::new(&mut world)
.transmute_filtered::<(Entity, Has<B>), Added<A>>(&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(); world.clear_trackers();
let entity_b = world.spawn((A(0), B(0))).id(); 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(); world.clear_trackers();
assert!(query.get_single(&world).is_err()); assert!(query.single(&world).is_err());
} }
#[test] #[test]
@ -1922,15 +1980,15 @@ mod tests {
.transmute_filtered::<Entity, Changed<A>>(&world); .transmute_filtered::<Entity, Changed<A>>(&world);
let mut change_query = QueryState::<&mut A>::new(&mut 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(); 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] #[test]
@ -2017,7 +2075,7 @@ mod tests {
let query_2 = QueryState::<&B, Without<C>>::new(&mut world); let query_2 = QueryState::<&B, Without<C>>::new(&mut world);
let mut new_query: QueryState<Entity, ()> = query_1.join_filtered(&world, &query_2); 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] #[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.| /// |[`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.| /// |[`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.| /// |[`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`). /// 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. /// 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`]\[[`_mut`][`get_mut`]]|O(1)|
/// |([`get_`][`get_many`])[`many`]|O(k)| /// |([`get_`][`get_many`])[`many`]|O(k)|
/// |([`get_`][`get_many_mut`])[`many_mut`]|O(k<sup>2</sup>)| /// |([`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)| /// |Archetype based filtering ([`With`], [`Without`], [`Or`])|O(a)|
/// |Change detection filtering ([`Added`], [`Changed`])|O(a + n)| /// |Change detection filtering ([`Added`], [`Changed`])|O(a + n)|
/// ///
@ -351,8 +351,8 @@ use core::{
/// [`get_many`]: Self::get_many /// [`get_many`]: Self::get_many
/// [`get_many_mut`]: Self::get_many_mut /// [`get_many_mut`]: Self::get_many_mut
/// [`get_mut`]: Self::get_mut /// [`get_mut`]: Self::get_mut
/// [`get_single`]: Self::get_single /// [`single`]: Self::single
/// [`get_single_mut`]: Self::get_single_mut /// [`single_mut`]: Self::single_mut
/// [`iter`]: Self::iter /// [`iter`]: Self::iter
/// [`iter_combinations`]: Self::iter_combinations /// [`iter_combinations`]: Self::iter_combinations
/// [`iter_combinations_mut`]: Self::iter_combinations_mut /// [`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>, /// friends_query: Query<&Friends>,
/// mut counter_query: Query<&mut Counter>, /// 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) { /// for mut counter in counter_query.iter_many_unique_inner(friends) {
/// println!("Friend's counter: {:?}", counter.value); /// println!("Friend's counter: {:?}", counter.value);
/// counter.value += 1; /// 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) 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. /// 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. /// 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)] /// # #[derive(Component)]
/// # struct PlayerScore(i32); /// # struct PlayerScore(i32);
/// fn player_scoring_system(query: Query<&PlayerScore>) { /// fn player_scoring_system(query: Query<&PlayerScore>) {
/// match query.get_single() { /// match query.single() {
/// Ok(PlayerScore(score)) => { /// Ok(PlayerScore(score)) => {
/// println!("Score: {}", score); /// println!("Score: {}", score);
/// } /// }
@ -1765,43 +1735,16 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// ///
/// # See also /// # See also
/// ///
/// - [`get_single_mut`](Self::get_single_mut) to get the mutable query item. /// - [`single_mut`](Self::single_mut) to get the mutable query item.
/// - [`single`](Self::single) for the panicking version.
#[inline] #[inline]
pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> { pub fn single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
self.as_readonly().get_single_inner() self.as_readonly().single_inner()
} }
/// Returns a single query item when there is exactly one entity matching the query. /// A deprecated alias for [`single`](Self::single).
/// #[deprecated(note = "Please use `single` instead")]
/// # Panics pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
/// self.single()
/// 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()
} }
/// Returns a single query item when there is exactly one entity matching the query. /// 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); /// # struct Health(u32);
/// # /// #
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) { /// 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; /// health.0 += 1;
/// } /// }
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system); /// # 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 /// # See also
/// ///
/// - [`get_single`](Self::get_single) to get the read-only query item. /// - [`single`](Self::single) to get the read-only query item.
/// - [`single_mut`](Self::single_mut) for the panicking version.
#[inline] #[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> { 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. /// 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. /// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
/// ///
/// # Panics /// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
///
/// This method panics if the number of query items is **not** exactly one.
/// ///
/// # Example /// # Example
/// ///
@ -1852,7 +1798,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// # struct Health(u32); /// # struct Health(u32);
/// # /// #
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) { /// 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; /// health.0 += 1;
/// } /// }
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system); /// # 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 /// # 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`](Self::single) to get the read-only query item.
/// - [`single_mut`](Self::single_mut) to get the mutable 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. /// - [`single_inner`](Self::single_inner) for the panicking version.
#[inline] #[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 mut query = self.into_iter();
let first = query.next(); let first = query.next();
let extra = query.next().is_some(); 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))); /// # world.spawn((A(10), B(5)));
/// # /// #
/// fn reusable_function(lens: &mut QueryLens<&A>) { /// 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. /// // 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))); /// # world.spawn((A(10), B(5)));
/// # /// #
/// fn reusable_function(mut lens: QueryLens<&A>) { /// 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. /// // 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) state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
}; };
let single = query let single = query
.get_single_inner() .single_inner()
.expect("The query was expected to contain exactly one matching entity."); .expect("The query was expected to contain exactly one matching entity.");
Single { Single {
item: single, item: single,
@ -432,7 +432,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
world.change_tick(), world.change_tick(),
) )
}; };
let is_valid = query.get_single_inner().is_ok(); let is_valid = query.single_inner().is_ok();
if !is_valid { if !is_valid {
system_meta.try_warn_param::<Self>(); system_meta.try_warn_param::<Self>();
} }
@ -474,7 +474,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
let query = unsafe { let query = unsafe {
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick) 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 { Ok(single) => Some(Single {
item: single, item: single,
_filter: PhantomData, _filter: PhantomData,
@ -500,7 +500,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
world.change_tick(), world.change_tick(),
) )
}; };
let result = query.get_single_inner(); let result = query.single_inner();
let is_valid = !matches!(result, Err(QuerySingleError::MultipleEntities(_))); let is_valid = !matches!(result, Err(QuerySingleError::MultipleEntities(_)));
if !is_valid { if !is_valid {
system_meta.try_warn_param::<Self>(); system_meta.try_warn_param::<Self>();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -115,7 +115,7 @@ fn sprite_picking(
-transform.translation().z -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)| { for (pointer, location) in pointers.iter().filter_map(|(pointer, pointer_location)| {
pointer_location.location().map(|loc| (pointer, loc)) 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 // TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows let scale_factor = windows
.get_single() .single()
.map(|window| window.resolution.scale_factor()) .map(|window| window.resolution.scale_factor())
.unwrap_or(1.0); .unwrap_or(1.0);
let scaling = GlobalTransform::from_scale(Vec2::splat(scale_factor.recip()).extend(1.)); 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 // TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows let scale_factor = windows
.get_single() .single()
.ok() .ok()
.map(|window| window.resolution.scale_factor()) .map(|window| window.resolution.scale_factor())
.or(*last_scale_factor) .or(*last_scale_factor)

View File

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

View File

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

View File

@ -84,7 +84,7 @@ pub fn ui_picking(
.map(|(entity, camera, _)| { .map(|(entity, camera, _)| {
( (
entity, entity,
camera.target.normalize(primary_window.get_single().ok()), camera.target.normalize(primary_window.single().ok()),
) )
}) })
.filter_map(|(entity, target)| Some(entity).zip(target)) .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> { impl<'w, 's> DefaultUiCamera<'w, 's> {
pub fn get(&self) -> Option<Entity> { 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 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() { if !self.default_cameras.is_empty() {
once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed.")); 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>>, 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; return;
}; };
let Some(adapter) = adapters.get_mut(&primary_window_id) else { 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 let mut query = self
.world_mut() .world_mut()
.query_filtered::<Entity, With<PrimaryWindow>>(); .query_filtered::<Entity, With<PrimaryWindow>>();
let entity = query.single(&self.world()); let entity = query.single(&self.world()).unwrap();
self.world_mut() self.world_mut()
.entity_mut(entity) .entity_mut(entity)
.remove::<RawHandleWrapper>(); .remove::<RawHandleWrapper>();
@ -571,7 +571,7 @@ impl<T: Event> WinitAppRunnerState<T> {
// handle wrapper removed when the app was suspended. // handle wrapper removed when the app was suspended.
let mut query = self.world_mut() let mut query = self.world_mut()
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>(); .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 window = window.clone();
let mut create_window = let mut create_window =

View File

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

View File

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

View File

@ -78,7 +78,7 @@ fn input_handler(
time: Res<Time>, time: Res<Time>,
) { ) {
if keyboard_input.just_pressed(KeyCode::Space) { 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(); let mesh = meshes.get_mut(mesh_handle).unwrap();
toggle_texture(mesh); toggle_texture(mesh);
} }

View File

@ -212,7 +212,7 @@ fn drag_drop_image(
mat.base_color_texture = Some(new_image.clone()); mat.base_color_texture = Some(new_image.clone());
// Despawn the image viewer instructions // 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(); commands.entity(text_entity).despawn();
} }
} }

View File

@ -258,7 +258,7 @@ fn get_async_loading_state(
// If loaded, change the state. // If loaded, change the state.
if is_loaded { if is_loaded {
next_loading_state.set(LoadingState::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); "Loaded!".clone_into(&mut **text);
} }
} }

View File

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

View File

@ -165,7 +165,7 @@ fn screen_shake(
} }
} else { } else {
// return camera to the latest position of player (it's fixed in this example case) // 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 sub_view = camera.sub_camera_view.as_mut().unwrap();
let target = screen_shake.latest_position.unwrap(); let target = screen_shake.latest_position.unwrap();
sub_view sub_view

View File

@ -69,7 +69,7 @@ fn list_all_named_entities(
text_string.push_str(&format!("{:?}\n", name)); 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); *text = Text::new(text_string);
} else { } else {
commands.spawn(( commands.spawn((

View File

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

View File

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

View File

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

View File

@ -112,7 +112,7 @@ fn run_camera_controller(
) { ) {
let dt = time.delta_secs(); 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; return;
}; };

View File

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

View File

@ -333,7 +333,7 @@ fn mouse_handler(
mut rng: Local<Option<ChaCha8Rng>>, mut rng: Local<Option<ChaCha8Rng>>,
mut wave: Local<usize>, mut wave: Local<usize>,
) { ) {
let Ok(window) = window.get_single() else { let Ok(window) = window.single() else {
return; 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)>) { 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; return;
}; };

View File

@ -26,7 +26,7 @@ fn setup(
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
window: Query<&Window>, window: Query<&Window>,
) { ) -> Result {
// circular base // circular base
commands.spawn(( commands.spawn((
Mesh3d(meshes.add(Circle::new(4.0))), Mesh3d(meshes.add(Circle::new(4.0))),
@ -56,7 +56,7 @@ fn setup(
} }
// cameras // cameras
let window = window.single(); let window = window.single()?;
let width = window.resolution.width() / CAMERA_COLS as f32 * window.resolution.scale_factor(); 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 height = window.resolution.height() / CAMERA_ROWS as f32 * window.resolution.scale_factor();
let mut i = 0; let mut i = 0;
@ -83,6 +83,7 @@ fn setup(
i += 1; i += 1;
} }
} }
Ok(())
} }
fn rotate_cameras(time: Res<Time>, mut query: Query<&mut Transform, With<Camera>>) { 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 // System for rotating and translating the camera
fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<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.rotate_z(time.delta_secs() * 0.5);
*camera_transform = *camera_transform =
*camera_transform * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_secs()); *camera_transform * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_secs());

View File

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

View File

@ -92,7 +92,7 @@ fn handle_input(
mut dir: ResMut<ResizeDir>, mut dir: ResMut<ResizeDir>,
example_text: Query<Entity, With<Text>>, example_text: Query<Entity, With<Text>>,
mut writer: TextUiWriter, mut writer: TextUiWriter,
) { ) -> Result {
use LeftClickAction::*; use LeftClickAction::*;
if input.just_pressed(KeyCode::KeyA) { if input.just_pressed(KeyCode::KeyA) {
*action = match *action { *action = match *action {
@ -100,7 +100,7 @@ fn handle_input(
Resize => Nothing, Resize => Nothing,
Nothing => Move, Nothing => Move,
}; };
*writer.text(example_text.single(), 4) = format!("{:?}", *action); *writer.text(example_text.single()?, 4) = format!("{:?}", *action);
} }
if input.just_pressed(KeyCode::KeyS) { if input.just_pressed(KeyCode::KeyS) {
@ -108,13 +108,15 @@ fn handle_input(
.0 .0
.checked_sub(1) .checked_sub(1)
.unwrap_or(DIRECTIONS.len().saturating_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) { if input.just_pressed(KeyCode::KeyD) {
dir.0 = (dir.0 + 1) % DIRECTIONS.len(); 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( 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>>) { fn spell_casting(mut player: Query<&mut Player>, keyboard_input: Res<ButtonInput<KeyCode>>) {
if keyboard_input.just_pressed(KeyCode::Space) { 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; return;
}; };
@ -76,7 +76,7 @@ fn test_player_spawn() {
// Now that the startup systems have run, we can check if the player has // Now that the startup systems have run, we can check if the player has
// spawned as expected. // spawned as expected.
let expected = Player::default(); 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!(actual.is_ok(), "There should be exactly 1 player.");
assert_eq!( assert_eq!(
expected.mana, expected.mana,
@ -98,7 +98,11 @@ fn test_spell_casting() {
app.update(); app.update();
let expected = Player::default(); 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!( assert_eq!(
expected.mana - 1, expected.mana - 1,
actual.mana, actual.mana,
@ -112,7 +116,11 @@ fn test_spell_casting() {
app.update(); app.update();
// No extra spells have been cast, so no mana should have been used. // 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!( assert_eq!(
expected.mana - 1, expected.mana - 1,
after_keypress_event.mana, after_keypress_event.mana,
@ -127,6 +135,10 @@ fn test_window_title() {
app.update(); 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!"); 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>) { 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; return;
}; };