Skip to content

Commit fa2b5f2

Browse files
Add documentation to ParamSet (#6998)
# Objective Fixes #4729. Continuation of #4854. ## Solution Add documentation to `ParamSet` and its methods. Includes examples suggested by community members in the original PR. Co-authored-by: Nanox19435 <[email protected]> Co-authored-by: JoJoJet <[email protected]>
1 parent ca87830 commit fa2b5f2

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,17 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
222222
for (i, param) in params.iter().enumerate() {
223223
let fn_name = Ident::new(&format!("p{i}"), Span::call_site());
224224
let index = Index::from(i);
225+
let ordinal = match i {
226+
1 => "1st".to_owned(),
227+
2 => "2nd".to_owned(),
228+
3 => "3rd".to_owned(),
229+
x => format!("{x}th"),
230+
};
231+
let comment =
232+
format!("Gets exclusive access to the {ordinal} parameter in this [`ParamSet`].");
225233
param_fn_muts.push(quote! {
234+
#[doc = #comment]
235+
/// No other parameters may be accessed while this one is active.
226236
pub fn #fn_name<'a>(&'a mut self) -> SystemParamItem<'a, 'a, #param> {
227237
// SAFETY: systems run without conflicts with other systems.
228238
// Conflicting params in ParamSet are not accessible at the same time

crates/bevy_ecs/src/system/system_param.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,114 @@ fn assert_component_access_compatibility(
239239
query_type, filter_type, system_name, accesses);
240240
}
241241

242+
/// A collection of potentially conflicting [`SystemParam`]s allowed by disjoint access.
243+
///
244+
/// Allows systems to safely access and interact with up to 8 mutually exclusive [`SystemParam`]s, such as
245+
/// two queries that reference the same mutable data or an event reader and writer of the same type.
246+
///
247+
/// Each individual [`SystemParam`] can be accessed by using the functions `p0()`, `p1()`, ..., `p7()`,
248+
/// according to the order they are defined in the `ParamSet`. This ensures that there's either
249+
/// only one mutable reference to a parameter at a time or any number of immutable references.
250+
///
251+
/// # Examples
252+
///
253+
/// The following system mutably accesses the same component two times,
254+
/// which is not allowed due to rust's mutability rules.
255+
///
256+
/// ```should_panic
257+
/// # use bevy_ecs::prelude::*;
258+
/// #
259+
/// # #[derive(Component)]
260+
/// # struct Health;
261+
/// #
262+
/// # #[derive(Component)]
263+
/// # struct Enemy;
264+
/// #
265+
/// # #[derive(Component)]
266+
/// # struct Ally;
267+
/// #
268+
/// // This will panic at runtime when the system gets initialized.
269+
/// fn bad_system(
270+
/// mut enemies: Query<&mut Health, With<Enemy>>,
271+
/// mut allies: Query<&mut Health, With<Ally>>,
272+
/// ) {
273+
/// // ...
274+
/// }
275+
/// #
276+
/// # let mut bad_system_system = bevy_ecs::system::IntoSystem::into_system(bad_system);
277+
/// # let mut world = World::new();
278+
/// # bad_system_system.initialize(&mut world);
279+
/// # bad_system_system.run((), &mut world);
280+
/// ```
281+
///
282+
/// Conflicing `SystemParam`s like these can be placed in a `ParamSet`,
283+
/// which leverages the borrow checker to ensure that only one of the contained parameters are accessed at a given time.
284+
///
285+
/// ```
286+
/// # use bevy_ecs::prelude::*;
287+
/// #
288+
/// # #[derive(Component)]
289+
/// # struct Health;
290+
/// #
291+
/// # #[derive(Component)]
292+
/// # struct Enemy;
293+
/// #
294+
/// # #[derive(Component)]
295+
/// # struct Ally;
296+
/// #
297+
/// // Given the following system
298+
/// fn fancy_system(
299+
/// mut set: ParamSet<(
300+
/// Query<&mut Health, With<Enemy>>,
301+
/// Query<&mut Health, With<Ally>>,
302+
/// )>
303+
/// ) {
304+
/// // This will access the first `SystemParam`.
305+
/// for mut health in set.p0().iter_mut() {
306+
/// // Do your fancy stuff here...
307+
/// }
308+
///
309+
/// // The second `SystemParam`.
310+
/// // This would fail to compile if the previous parameter was still borrowed.
311+
/// for mut health in set.p1().iter_mut() {
312+
/// // Do even fancier stuff here...
313+
/// }
314+
/// }
315+
/// # bevy_ecs::system::assert_is_system(fancy_system);
316+
/// ```
317+
///
318+
/// Of course, `ParamSet`s can be used with any kind of `SystemParam`, not just [queries](Query).
319+
///
320+
/// ```
321+
/// # use bevy_ecs::prelude::*;
322+
/// #
323+
/// # struct MyEvent;
324+
/// # impl MyEvent {
325+
/// # pub fn new() -> Self { Self }
326+
/// # }
327+
/// fn event_system(
328+
/// mut set: ParamSet<(
329+
/// // `EventReader`s and `EventWriter`s conflict with each other,
330+
/// // since they both access the event queue resource for `MyEvent`.
331+
/// EventReader<MyEvent>,
332+
/// EventWriter<MyEvent>,
333+
/// // `&World` reads the entire world, so a `ParamSet` is the only way
334+
/// // that it can be used in the same system as any mutable accesses.
335+
/// &World,
336+
/// )>,
337+
/// ) {
338+
/// for event in set.p0().iter() {
339+
/// // ...
340+
/// # let _event = event;
341+
/// }
342+
/// set.p1().send(MyEvent::new());
343+
///
344+
/// let entities = set.p2().entities();
345+
/// // ...
346+
/// # let _entities = entities;
347+
/// }
348+
/// # bevy_ecs::system::assert_is_system(event_system);
349+
/// ```
242350
pub struct ParamSet<'w, 's, T: SystemParam> {
243351
param_states: &'s mut T::State,
244352
world: &'w World,

0 commit comments

Comments
 (0)