Skip to content

Conversation

@chescock
Copy link
Contributor

Objective

Let functions that take StaticSystemInput<I> be used in APIs that require IntoSystem<I, O, M>.

Higher-order systems that are generic in their input type need to wrap their input in StaticSystemInput. Unfortunately, that means they are not usable with World::run_system_cached or Schedule::add_systems, since those require In = () and not In = StaticSystemInput<()>.

Solution

Add an associated type SystemInput::Underlying that is equal to Self (up to lifetimes) for all input types other than StaticSystemInput<I>, and equal to I::Underlying for StaticSystemInput<I>. Use that associated type for <FunctionSystem as System>::In.

That means a function taking StaticSystemInput<()> will be turned into a system with In = () instead of In = StaticSystemInput<()>, making it usable for World::run_system_cached and Schedule::add_systems.

Testing

Updated the doc example of a safe PipeSystem to call the resulting system with World::run_system_cached instead of needing to call System::run explicitly.

Added unit tests passing a function taking StaticSystemInput<()> to World::run_system_cached and Schedule::add_systems.

@chescock chescock added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Nov 23, 2025
@cBournhonesque
Copy link
Contributor

Would this work instead?
https://github.com/cBournhonesque/bevy/pull/4/files
It seems easier to follow to me than the adding an Underlying associated type

@chescock
Copy link
Contributor Author

Would this work instead? https://github.com/cBournhonesque/bevy/pull/4/files It seems easier to follow to me than the adding an Underlying associated type

Yeah, that would make run_system_cached work!

We'd need to make the same change in a lot of places to make it work seamlessly everywhere, though, including IntoScheduleConfigs, IntoObserverSystem, run_if, and (un)register_system_cached. And it would be easy to forget it we added a new place. Doing it centrally like this means we know it works everywhere, and makes the method signatures simpler.

@cBournhonesque
Copy link
Contributor

I do think the approach of changing the call sites is better, but I won't block on this. I think this is a useful change!

Copy link
Contributor

@ItsDoot ItsDoot left a comment

Choose a reason for hiding this comment

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

Looks good, only thing I'd add is a test for a tuple of StaticSystemInputs and one for a mixed tuple of StaticSystemInput + a normal one.

@ItsDoot ItsDoot added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Nov 28, 2025
@hymm
Copy link
Contributor

hymm commented Nov 28, 2025

I haven't done a full review yet, but if we go with this approach I'd like to bikeshed Underlying I don't think it's very descriptive to what it actually is.

@chescock
Copy link
Contributor Author

I haven't done a full review yet, but if we go with this approach I'd like to bikeshed Underlying I don't think it's very descriptive to what it actually is.

Yeah, if anyone has a good name, I'd be happy to update it!

I usually use Inner for something that removes a layer of type like this, but that was already taken by the In<T> -> T mapping, so I just reached for a thesaurus :).

For a more brute-force approach, we could do something like type WithoutStaticSystemInput;.

Mostly I'm hoping that nobody needs to interact with this type very often, and that the doc comment mentioning StaticSystemInput will clarify things when they do.

Looks good, only thing I'd add is a test for a tuple of StaticSystemInputs and one for a mixed tuple of StaticSystemInput + a normal one.

I added one test with a tuple of two SSIs and one normal one, since that should cover both cases. They do work, although I'm not actually sure what they're useful for. And you can always use StaticSystemInput<(In<T>, I1, I2)> in place of (In<T>, StaticSystemInput<I1>, StaticSystemInput<I2>).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants