Skip to content

Commit c0a93aa

Browse files
Rename system chaining to system piping (#6230)
# Objective > System chaining is a confusing name: it implies the ability to construct non-linear graphs, and suggests a sense of system ordering that is only incidentally true. Instead, it actually works by passing data from one system to the next, much like the pipe operator. > In the accepted [stageless RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/45-stageless.md), this concept is renamed to piping, and "system chaining" is used to construct groups of systems with ordering dependencies between them. Fixes #6225. ## Changelog System chaining has been renamed to system piping to improve clarity (and free up the name for new ordering APIs). ## Migration Guide The `.chain(handler_system)` method on systems is now `.pipe(handler_system)`. The `IntoChainSystem` trait is now `IntoPipeSystem`, and the `ChainSystem` struct is now `PipeSystem`.
1 parent 6ce7ce2 commit c0a93aa

File tree

8 files changed

+62
-60
lines changed

8 files changed

+62
-60
lines changed

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -872,12 +872,12 @@ category = "ECS (Entity Component System)"
872872
wasm = false
873873

874874
[[example]]
875-
name = "system_chaining"
876-
path = "examples/ecs/system_chaining.rs"
875+
name = "system_piping"
876+
path = "examples/ecs/system_piping.rs"
877877

878-
[package.metadata.example.system_chaining]
879-
name = "System Chaining"
880-
description = "Chain two systems together, specifying a return type in a system (such as `Result`)"
878+
[package.metadata.example.system_piping]
879+
name = "System Piping"
880+
description = "Pipe the output of one system into a second, allowing you to handle any errors gracefully"
881881
category = "ECS (Entity Component System)"
882882
wasm = false
883883

crates/bevy_ecs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub mod prelude {
3838
Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage,
3939
},
4040
system::{
41-
adapter as system_adapter, Commands, In, IntoChainSystem, IntoSystem, Local, NonSend,
41+
adapter as system_adapter, Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend,
4242
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut,
4343
Resource, System, SystemParamFunction,
4444
},

crates/bevy_ecs/src/schedule/state.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
44
SystemSet,
55
},
6-
system::{In, IntoChainSystem, Local, Res, ResMut, Resource},
6+
system::{In, IntoPipeSystem, Local, Res, ResMut, Resource},
77
};
88
use std::{
99
any::TypeId,
@@ -79,7 +79,7 @@ where
7979
(move |state: Res<State<T>>| {
8080
state.stack.last().unwrap() == &pred && state.transition.is_none()
8181
})
82-
.chain(should_run_adapter::<T>)
82+
.pipe(should_run_adapter::<T>)
8383
.after(DriverLabel::of::<T>())
8484
}
8585

@@ -95,7 +95,7 @@ where
9595
Some(_) => false,
9696
None => *is_inactive,
9797
})
98-
.chain(should_run_adapter::<T>)
98+
.pipe(should_run_adapter::<T>)
9999
.after(DriverLabel::of::<T>())
100100
}
101101

@@ -118,7 +118,7 @@ where
118118
Some(_) => false,
119119
None => *is_in_stack,
120120
})
121-
.chain(should_run_adapter::<T>)
121+
.pipe(should_run_adapter::<T>)
122122
.after(DriverLabel::of::<T>())
123123
}
124124

@@ -133,7 +133,7 @@ where
133133
_ => false,
134134
})
135135
})
136-
.chain(should_run_adapter::<T>)
136+
.pipe(should_run_adapter::<T>)
137137
.after(DriverLabel::of::<T>())
138138
}
139139

@@ -148,7 +148,7 @@ where
148148
_ => false,
149149
})
150150
})
151-
.chain(should_run_adapter::<T>)
151+
.pipe(should_run_adapter::<T>)
152152
.after(DriverLabel::of::<T>())
153153
}
154154

@@ -162,7 +162,7 @@ where
162162
_ => false,
163163
})
164164
})
165-
.chain(should_run_adapter::<T>)
165+
.pipe(should_run_adapter::<T>)
166166
.after(DriverLabel::of::<T>())
167167
}
168168

@@ -176,7 +176,7 @@ where
176176
_ => false,
177177
})
178178
})
179-
.chain(should_run_adapter::<T>)
179+
.pipe(should_run_adapter::<T>)
180180
.after(DriverLabel::of::<T>())
181181
}
182182

crates/bevy_ecs/src/system/function_system.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
499499
///
500500
/// # Example
501501
///
502-
/// To create something like [`ChainSystem`], but in entirely safe code.
502+
/// To create something like [`PipeSystem`], but in entirely safe code.
503503
///
504504
/// ```rust
505505
/// use std::num::ParseIntError;
@@ -510,8 +510,8 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
510510
/// // Unfortunately, we need all of these generics. `A` is the first system, with its
511511
/// // parameters and marker type required for coherence. `B` is the second system, and
512512
/// // the other generics are for the input/output types of `A` and `B`.
513-
/// /// Chain creates a new system which calls `a`, then calls `b` with the output of `a`
514-
/// pub fn chain<AIn, Shared, BOut, A, AParam, AMarker, B, BParam, BMarker>(
513+
/// /// Pipe creates a new system which calls `a`, then calls `b` with the output of `a`
514+
/// pub fn pipe<AIn, Shared, BOut, A, AParam, AMarker, B, BParam, BMarker>(
515515
/// mut a: A,
516516
/// mut b: B,
517517
/// ) -> impl FnMut(In<AIn>, ParamSet<(SystemParamItem<AParam>, SystemParamItem<BParam>)>) -> BOut
@@ -529,15 +529,15 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
529529
/// }
530530
/// }
531531
///
532-
/// // Usage example for `chain`:
532+
/// // Usage example for `pipe`:
533533
/// fn main() {
534534
/// let mut world = World::default();
535535
/// world.insert_resource(Message("42".to_string()));
536536
///
537-
/// // chain the `parse_message_system`'s output into the `filter_system`s input
538-
/// let mut chained_system = IntoSystem::into_system(chain(parse_message, filter));
539-
/// chained_system.initialize(&mut world);
540-
/// assert_eq!(chained_system.run((), &mut world), Some(42));
537+
/// // pipe the `parse_message_system`'s output into the `filter_system`s input
538+
/// let mut piped_system = IntoSystem::into_system(pipe(parse_message, filter));
539+
/// piped_system.initialize(&mut world);
540+
/// assert_eq!(piped_system.run((), &mut world), Some(42));
541541
/// }
542542
///
543543
/// #[derive(Resource)]
@@ -551,7 +551,7 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
551551
/// result.ok().filter(|&n| n < 100)
552552
/// }
553553
/// ```
554-
/// [`ChainSystem`]: crate::system::ChainSystem
554+
/// [`PipeSystem`]: crate::system::PipeSystem
555555
/// [`ParamSet`]: crate::system::ParamSet
556556
pub trait SystemParamFunction<In, Out, Param: SystemParam, Marker>: Send + Sync + 'static {
557557
fn run(&mut self, input: In, param_value: SystemParamItem<Param>) -> Out;

crates/bevy_ecs/src/system/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,17 @@ mod function_system;
7575
mod query;
7676
#[allow(clippy::module_inception)]
7777
mod system;
78-
mod system_chaining;
7978
mod system_param;
79+
mod system_piping;
8080

8181
pub use commands::*;
8282
pub use exclusive_function_system::*;
8383
pub use exclusive_system_param::*;
8484
pub use function_system::*;
8585
pub use query::*;
8686
pub use system::*;
87-
pub use system_chaining::*;
8887
pub use system_param::*;
88+
pub use system_piping::*;
8989

9090
/// Ensure that a given function is a system
9191
///

crates/bevy_ecs/src/system/system_chaining.rs renamed to crates/bevy_ecs/src/system/system_piping.rs

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ use crate::{
77
};
88
use std::borrow::Cow;
99

10-
/// A [`System`] that chains two systems together, creating a new system that routes the output of
11-
/// the first system into the input of the second system, yielding the output of the second system.
10+
/// A [`System`] created by piping the output of the first system into the input of the second.
1211
///
13-
/// Given two systems `A` and `B`, A may be chained with `B` as `A.chain(B)` if the output type of `A` is
12+
/// This can be repeated indefinitely, but system pipes cannot branch: the output is consumed by the receiving system.
13+
///
14+
/// Given two systems `A` and `B`, A may be piped into `B` as `A.pipe(B)` if the output type of `A` is
1415
/// equal to the input type of `B`.
1516
///
1617
/// Note that for [`FunctionSystem`](crate::system::FunctionSystem)s the output is the return value
@@ -28,10 +29,10 @@ use std::borrow::Cow;
2829
/// let mut world = World::default();
2930
/// world.insert_resource(Message("42".to_string()));
3031
///
31-
/// // chain the `parse_message_system`'s output into the `filter_system`s input
32-
/// let mut chained_system = parse_message_system.chain(filter_system);
33-
/// chained_system.initialize(&mut world);
34-
/// assert_eq!(chained_system.run((), &mut world), Some(42));
32+
/// // pipe the `parse_message_system`'s output into the `filter_system`s input
33+
/// let mut piped_system = parse_message_system.pipe(filter_system);
34+
/// piped_system.initialize(&mut world);
35+
/// assert_eq!(piped_system.run((), &mut world), Some(42));
3536
/// }
3637
///
3738
/// #[derive(Resource)]
@@ -45,15 +46,15 @@ use std::borrow::Cow;
4546
/// result.ok().filter(|&n| n < 100)
4647
/// }
4748
/// ```
48-
pub struct ChainSystem<SystemA, SystemB> {
49+
pub struct PipeSystem<SystemA, SystemB> {
4950
system_a: SystemA,
5051
system_b: SystemB,
5152
name: Cow<'static, str>,
5253
component_access: Access<ComponentId>,
5354
archetype_component_access: Access<ArchetypeComponentId>,
5455
}
5556

56-
impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for ChainSystem<SystemA, SystemB> {
57+
impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for PipeSystem<SystemA, SystemB> {
5758
type In = SystemA::In;
5859
type Out = SystemB::Out;
5960

@@ -121,33 +122,34 @@ impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for ChainSystem
121122
}
122123
}
123124

124-
/// An extension trait providing the [`IntoChainSystem::chain`] method for convenient [`System`]
125-
/// chaining.
125+
/// An extension trait providing the [`IntoPipeSystem::pipe`] method to pass input from one system into the next.
126+
///
127+
/// The first system must have return type `T`
128+
/// and the second system must have [`In<T>`](crate::system::In) as its first system parameter.
126129
///
127-
/// This trait is blanket implemented for all system pairs that fulfill the chaining requirement.
130+
/// This trait is blanket implemented for all system pairs that fulfill the type requirements.
128131
///
129-
/// See [`ChainSystem`].
130-
pub trait IntoChainSystem<ParamA, Payload, SystemB, ParamB, Out>:
132+
/// See [`PipeSystem`].
133+
pub trait IntoPipeSystem<ParamA, Payload, SystemB, ParamB, Out>:
131134
IntoSystem<(), Payload, ParamA> + Sized
132135
where
133136
SystemB: IntoSystem<Payload, Out, ParamB>,
134137
{
135-
/// Chain this system `A` with another system `B` creating a new system that feeds system A's
136-
/// output into system `B`, returning the output of system `B`.
137-
fn chain(self, system: SystemB) -> ChainSystem<Self::System, SystemB::System>;
138+
/// Pass the output of this system `A` into a second system `B`, creating a new compound system.
139+
fn pipe(self, system: SystemB) -> PipeSystem<Self::System, SystemB::System>;
138140
}
139141

140142
impl<SystemA, ParamA, Payload, SystemB, ParamB, Out>
141-
IntoChainSystem<ParamA, Payload, SystemB, ParamB, Out> for SystemA
143+
IntoPipeSystem<ParamA, Payload, SystemB, ParamB, Out> for SystemA
142144
where
143145
SystemA: IntoSystem<(), Payload, ParamA>,
144146
SystemB: IntoSystem<Payload, Out, ParamB>,
145147
{
146-
fn chain(self, system: SystemB) -> ChainSystem<SystemA::System, SystemB::System> {
148+
fn pipe(self, system: SystemB) -> PipeSystem<SystemA::System, SystemB::System> {
147149
let system_a = IntoSystem::into_system(self);
148150
let system_b = IntoSystem::into_system(system);
149-
ChainSystem {
150-
name: Cow::Owned(format!("Chain({}, {})", system_a.name(), system_b.name())),
151+
PipeSystem {
152+
name: Cow::Owned(format!("Pipe({}, {})", system_a.name(), system_b.name())),
151153
system_a,
152154
system_b,
153155
archetype_component_access: Default::default(),
@@ -156,7 +158,7 @@ where
156158
}
157159
}
158160

159-
/// A collection of common adapters for [chaining](super::ChainSystem) the result of a system.
161+
/// A collection of common adapters for [piping](super::PipeSystem) the result of a system.
160162
pub mod adapter {
161163
use crate::system::In;
162164
use std::fmt::Debug;
@@ -168,9 +170,9 @@ pub mod adapter {
168170
/// use bevy_ecs::prelude::*;
169171
///
170172
/// return1
171-
/// .chain(system_adapter::new(u32::try_from))
172-
/// .chain(system_adapter::unwrap)
173-
/// .chain(print);
173+
/// .pipe(system_adapter::new(u32::try_from))
174+
/// .pipe(system_adapter::unwrap)
175+
/// .pipe(print);
174176
///
175177
/// fn return1() -> u64 { 1 }
176178
/// fn print(In(x): In<impl std::fmt::Debug>) {
@@ -204,7 +206,7 @@ pub mod adapter {
204206
/// .add_system_to_stage(
205207
/// CoreStage::Update,
206208
/// // Panic if the load system returns an error.
207-
/// load_save_system.chain(system_adapter::unwrap)
209+
/// load_save_system.pipe(system_adapter::unwrap)
208210
/// )
209211
/// // ...
210212
/// # ;
@@ -224,7 +226,7 @@ pub mod adapter {
224226
res.unwrap()
225227
}
226228

227-
/// System adapter that ignores the output of the previous system in a chain.
229+
/// System adapter that ignores the output of the previous system in a pipe.
228230
/// This is useful for fallible systems that should simply return early in case of an `Err`/`None`.
229231
///
230232
/// # Examples
@@ -248,7 +250,7 @@ pub mod adapter {
248250
/// .add_system_to_stage(
249251
/// CoreStage::Update,
250252
/// // If the system fails, just move on and try again next frame.
251-
/// fallible_system.chain(system_adapter::ignore)
253+
/// fallible_system.pipe(system_adapter::ignore)
252254
/// )
253255
/// // ...
254256
/// # ;
@@ -278,8 +280,8 @@ pub mod adapter {
278280
unimplemented!()
279281
}
280282

281-
assert_is_system(returning::<Result<u32, std::io::Error>>.chain(unwrap));
282-
assert_is_system(returning::<Option<()>>.chain(ignore));
283-
assert_is_system(returning::<&str>.chain(new(u64::from_str)).chain(unwrap));
283+
assert_is_system(returning::<Result<u32, std::io::Error>>.pipe(unwrap));
284+
assert_is_system(returning::<Option<()>>.pipe(ignore));
285+
assert_is_system(returning::<&str>.pipe(new(u64::from_str)).pipe(unwrap));
284286
}
285287
}

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ Example | Description
199199
[Removal Detection](../examples/ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame
200200
[Startup System](../examples/ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)
201201
[State](../examples/ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state
202-
[System Chaining](../examples/ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`)
203202
[System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state
204203
[System Parameter](../examples/ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam`
204+
[System Piping](../examples/ecs/system_piping.rs) | Pipe the output of one system into a second, allowing you to handle any errors gracefully
205205
[System Sets](../examples/ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion
206206
[Timers](../examples/ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state
207207

examples/ecs/system_chaining.rs renamed to examples/ecs/system_piping.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
//! Illustrates how to make a single system from multiple functions running in sequence and sharing
2-
//! their inputs and outputs.
1+
//! Illustrates how to make a single system from multiple functions running in sequence,
2+
//! passing the output of the first into the input of the next.
33
44
use anyhow::Result;
55
use bevy::prelude::*;
66

77
fn main() {
88
App::new()
99
.insert_resource(Message("42".to_string()))
10-
.add_system(parse_message_system.chain(handler_system))
10+
.add_system(parse_message_system.pipe(handler_system))
1111
.run();
1212
}
1313

0 commit comments

Comments
 (0)