I am confused.... still learning.
How can I assign a concrete struct to current_screen.
My PlayerScreen struct implements ScreenTrait. But compiler refuses.
expected type parameter S found struct PlayerScreen
Ok thanks. Add new() to the trait works. But there are three "requirements"
The trait should never be dynamic.
Inside program I have to be able to change to a new screentype.
the new() function can be different for different screens (different parameters), because they initialize differently. But maybe that is work-aroundable...
Inside program I have to be able to change to a new screentype.
Could you please clarify, what you mean by should never be dynamic. I assume it means should not be a trait object?
In the following, I assume it means should not be a trait object.
With our current appproach I don't think this will be possible, unfortunately (except if, everytime you change the screentype, you create a completely new Program and provide the appropriate type parameter , but I don't think this is what you want here).
Enums might be another possibility
If you have a fixed number of screentypes and consumers of your library/app don't need to add their own screentypes, you can maybe use an enum instead of a trait!?
This will also solve point 3 (initialize differently), because you can always explicitly construct an enum variant (e.g. Screentype::ScreenWithTwoParams(foo, bar)).
The easiest solution would be to simply not try to abstract over the constructor. Just make run accept an instance of the screen and leave instantiation to the caller.
As for the reason you got an error in the OP: In general how generics work is that inside the implementation body, your code must work for every possible type that can satisfy the bounds on the declared generic (even types that don't exist yet). Additionally, each generic type parameter will resolve to exactly one type. So in the OP, you can't assign a PlayerScreen to field current_screen. You can only assign S.
Make the implementation non-generic so the field is PlayerScreen
Make the creation of some S doable via trait bound
Make the caller provide the S
If it's ok for the type of Program<_> to change, supply some conversion methods.
If it's not, you need type erasure like a dyn Trait, where distinct base types can coerce to the same type-erased Box<dyn Trait> or such; or an enum, where multiple variants could hold distinct types.
I don't know what "the trait should never be dynamic" means either. Generally the only reason to distinctly want your trait to be non-object-safe is if you're future-proofing and want to protect the ability to add a trait unsafe item in the future without breaking downstream; but if that's what you meant, make the trait non-object-safe now by adding a Sized supertrait or a non-object-safe method.
It is - for me - a syntax thing... I know it is possible.
There is certainly a limited / fixed amount of screentypes. So enumeration / const traitbound is ok with me
My problem is the instantation syntax. And where the generics go.
if state_x currrent_screen = PlayerScreen::new(a, b, c);
if state_y current_screen = MenuScreen::new(q, r);
Of course! That is a straightforward solution. I will use that for now to get it working.
Now I can "redirect" the update routine.
Question: will this match statement be fast? Or can it be made faster?
The update (or the other routines which are there: handle_input etc.) will be called very often.
If you want to generically instantiate the type S, then you have to say how that can be done by adding a constructor-ish method to the trait, and then call that on the generic type S, not on some arbitrary concrete type. Playground.
But again, you shouldn't be doing this. Trying to abstract over construction of very different types is a straight-up anti-pattern. Just inject the dependency from the outside, like this.
Entry to and exit from hot, uninlined functions often accounts for a non-trivial fraction of execution time. Inlining these functions can provide small but easy speed wins.
So the code will look like this (note the #[inline] annotations directly above the method signatures):
In general, it is advisable to measure your performance first before doing optimizations, though.
There are a lot of options to do benchmarking in Rust. Here are some starting points you might want to look into:
this one might be very suitable for your kind of application (a game/GUI app?), but I haven't used it yet, so I can't really tell how useful it is in practice and how much effort it takes to set it up
Other than that, Brendan Gregg's Homepage is a gem when it comes to general performance tips (not Rust-specific).
There's e.g. a chapter on flamegraphs.
Yes, simple match statements like this are very fast and in general there is no reason to worry about them. It is faster, for example, than calling through a dyn trait object.
But it is up to you to define "fast". Perhaps you mean, "is this slow and often avoided", in which case the answer is no. But if you are micro optimizing a very performance critical code section, then it is worth measuring performance and perhaps looking at the assembly, as others have said.