Let me explain a bit more about "you can use resources that you forget to create and it will only crash at runtime instead of giving a compile time error".
Plugins are the main abstraction in Bevy. A plugin has two parts: build and run. Build creates stuff, and run uses stuff created by build and created by the build of other plugins running at the same time.
Build is pure side-effects, so the result type of build tells you nothing about what it builds. Therefore there are no types that can constrain what resources run uses, and therefore failing to create a resource that is used in run is a run-time, not compile-time error.
The alternative is the build returns a type representing the collection of resources it creates, and run's type is a collection of resources it uses. This requires some type level programming (a type level heterogeneous set). This is some of the simplest type level programming, but type level programming itself is quite foreign to most programmers. I assume this is why the Bevy developers went for the easier to write solution that doesn't enforce constraints at compile-time.
More generally, just like in regular programming some things are easier and some are harder to do in common type systems.
Your first example (integer constrained to a range) is harder because you need to solve linear inequations at compile-time, which is not a feature of most type systems (though see refined types).
You second example (must call a method) is relatively easy with linear types, as they express "must do something with this value".
> You second example (must call a method) is relatively easy with linear types, as they express "must do something with this value".
Interesting, could you possibly share an example on how that would look like in Rust? Haven't come across it yet, and would certainly help with some things.
Lets say we want to make sure if "MyStruct" is ever defined + created, we want to make sure the program somewhere calls "register_struct" with an instance of that struct.
Rust does not implement true linear types, only affine, so I believe your parent is mistaken.
You can do a dynamic check to encode this, but that's not super popular. You can also issue a warning, and in theory turn that warning into an error, but it may also trigger on code unrelated to the specific struct you want it to, so I don't think that's a full solution either, and others may use your code without the warning, getting less guarantees.
Plugins are the main abstraction in Bevy. A plugin has two parts: build and run. Build creates stuff, and run uses stuff created by build and created by the build of other plugins running at the same time.
Build is pure side-effects, so the result type of build tells you nothing about what it builds. Therefore there are no types that can constrain what resources run uses, and therefore failing to create a resource that is used in run is a run-time, not compile-time error.
The alternative is the build returns a type representing the collection of resources it creates, and run's type is a collection of resources it uses. This requires some type level programming (a type level heterogeneous set). This is some of the simplest type level programming, but type level programming itself is quite foreign to most programmers. I assume this is why the Bevy developers went for the easier to write solution that doesn't enforce constraints at compile-time.
More generally, just like in regular programming some things are easier and some are harder to do in common type systems.
Your first example (integer constrained to a range) is harder because you need to solve linear inequations at compile-time, which is not a feature of most type systems (though see refined types).
You second example (must call a method) is relatively easy with linear types, as they express "must do something with this value".