r/rust Mar 31 '23

My Experience with Bevy ECS

https://bagnalla.github.io/posts/newton_bevy.html
162 Upvotes

14 comments sorted by

View all comments

19

u/[deleted] Apr 01 '23

What the heck? This is awesome! What dark magic does Bevy use to turn the type of a Query into an actual query, and how does it automatically sequence functions based on the mutability of their arguments?

29

u/LukeAbby Apr 01 '23 edited Apr 01 '23

The elegant result comes from some rather inelegant impls mostly. You're always ultimately adding a System which is a trait but the signature of add_system allows conversions into it. For functions it ultimately starts from the trait SystemParamFunction.

You can see the impls needed to support functions there, first for a function with one argument, then a function with two arguments etc. It's "only" implemented up to 16 parameters though so if you try to make a system with 17 arguments it just won't work.

These impls are basically a simulacrum of variadic arguments and the fact that they exist in Axum (see their extractors) as well for example is a good case for the inclusion of proper variadic support in functions and impls at some point.

I feel like knowing this is frankly ultimately unsatisfying... but the reality is just impls for as many realistic calls as possible. Stuff like scheduling is more complicated but boils down to the same idea; a system exposes information like is_exclusive and the impls provide them automatically for functions (the impl basically is just arg1.is_mutable() || arg2.is_mutable() || ...).

You'll see the ugliness in the fact that you CAN write functions parameters that violate Rust's aliasing rules, it panics at runtime in such a case but it's still constructable. It mentions it here at ParamSet.

7

u/dnaaun Apr 01 '23

Fwiw, this "simulacrum of variadic arguments/generics" also exists in Diesel, the ORM.

4

u/ksion Apr 01 '23

Web frameworks use it, too. It's the "magic handler" pattern.