I am currently working on a game , and came across the issue of not being able to use multiple different defaults to be able to initialize different types of Components in bevy, and after working on it a bit finally came up with this implementation ,
pub trait Defaults : Sized+Clone where Self: 'static{
const DEFAULTS : &'static [Self];
fn defaults(no:usize)-> Self{
let length = Self::DEFAULTS.len();
if 0 < length && length < no {
return Self::DEFAULTS.get(no).unwrap().clone();
}else{
return Self::DEFAULTS.get(0).unwrap().clone();
}
}
}
Here instead of overriding the defaults function and returning the self with value ,we would override DEFAULTS variable with a reference to a slice
and retrieve the desired defaults via indexing .
This specific way of implementing allows for the
struct/enum/union which inheirits the trait can have an arbitrary number of Defaults which which are decided by it itself while overriding
Also handles for overflow / underflow of index of the slice to ensure the program doesn't panic by doing a simple check and returning 0 index ,if not available.
It's setup in such a way as to have minimum no of extra steps of code in the use ,to make it as easy to use ,which was a problem in my previous iterations (also could be just how I handled the iterations)
I have also seen some other people have the same issue ,so have thought to share , and maybe add it to rust after some refinement in implementation ,haven't contributed to open source till now ,hence submitting it here
Here are some questions for you to answer to help justify and flesh out this proposal:
What does it mean for a type to have multiple default values? If a generic function takes <T: Defaults>, how would it be using them?
What is an example of where this trait would be used? (Not just an implementation, but a use case. You say “I have also seen some other people have the same issue” — tell us about that in detail.)
Why should this trait be in the standard library — what interoperability benefit does it provide, or what commonly-written code would be simplified by having it? What is gained by putting it in std forever, that isn’t possible in a separate library?
What standard library types would implement this? Would every type that implements Default also implement Defaults?
How would it be implemented by generic types like [T; 2]?
Normally Default is used for initiating for a type ,it simply returns the Self created during override
A type having multiple defaults means that there is multiple options to initialize from rather than a singular option , simply an extension of the Default Trait
If a generic function Takes Defaults Trait it would behave the same way as Default with only exception being that calling the object would be
Object::defaults(num:usize)
//or
..defaults(num:usize)
//instead of
Objects::default()
//or
..defaults()
Specifically for a Game Dev using BEVY ,it is beneficial to setup different variants of Components(Structs/Enums) ,
player variants - currently I am using mainly for testing purposes ( to test out different player variables to get a feel for different player movements,which is very important for game feel ) - but can also be used for different player characters which will have different settings (gravity ,jumps,abilities,speed,damage,acceleration,etc.)
enemies (which can be used to programmatically spawn in the environment) ,all the different types of enemies can be constructed from defaults
platforms - programmatically generate terrain/ platforms (whether it be for procedural generation or manual level creation),
I mainly see it as a easy way to be able to initiate multiple different variants of a single object , in BEVY all objects are structs normally
But this logic can be extended to other domains as well such as
Databases which for Tables require different base default values of the same data type,
Different initialization conditions for a simulation
The reason I feel it should be in the standard library is that ,it feels like a natural extension of the default trait and opens up situational variable initialization instead of having manual initialization for items which are repeatedly initialized
I don't know exactly how much of a difference between a separate library and standard library would accomplish other than the accessibility and discoverability of the trait and it feels like the ability to have multiple default values feels like an natural extension of the Defaults ,and should be coupled with it -
(I am not sure because, I am not sure about the strength of these arguments in this context),
Commonly written code which could be simplified are recurring variable initialization ,other languages can just use an array like data type to do this , but rust practices this using Default trait and Defaults is an extension of the same functionality for multiple defaults as you can do in other languages.
Having multiple initializations can be a useful functionality and the easiest way to do so is through a Default Trait variant.
This is specifically useful for creating prefabs and explicitly calling the prefabs so that it is much easier to use different variants
This is opposed to the default definition in Docs where it says it is used for if you don't care explicitly what the value is but need a valid value
Which is the way it's used in enums , but this can be extended to have multiple default values ,to choose the initialization value of the enum specifically
The functionality of initializing the Types using Default trait ,can be expanded ,for specific initialization for values you use repeatedly and want to store
Every type which implements default can use defaults, and a special case of defaults can have defaults functionality,when no index is specified, return first index 0th default ,such as to have the any valid value implementation of default.
A generic type such as [ T ; 2 ] would implement it with basically no differently than Default itself ,with the only difference is
The syntax to call it having a index required..defaults(0) (not sure how to overload so it doesn't necessarily need an index to function ..defaults(), would return first default in that case..defaults(0),still relatively new to Rust ,but have used other programming languages)
The type needs to be able to be stored in a slice (as per the current implementation) and stored in a const slice reference to function (which I am unsure of how many types would fit into this, as this way of implementation currently works for me)
Some places I am repeating myself as the functionality it seems to be achieving seems very basic to me and not sure how else to phrase it.
This basically feels like a [Try]From<usize> for T impl. Why not make an enum that covers the usize values you want to use and require T: From<InitVariant> instead? This avoids the panic! on out-of-bounds initialization as well…
It also makes it easier to read at the use site what is intended (e.g., InitVariant::Level2 instead of just 2).
It seems that creating a default value based on an integer key in particular has very limited uses beyond your specific use case. If I call Foo::defaults(0), what does that mean? How does it differ from Foo::defaults(3)? Is it just arbitrary, or up to the implementer to document? In general interfaces based on magic constants are not great.
Even in your particular use case, it’s uncommon to encode the "defaults" statically like that. It may be okay in simple games and prototypes, but in general you want some more flexible data-driven way to store such entity archetypes.
But if you find the trait useful, make it a library and post to crates.io to – there doesn’t seem to be a reason it should be in std. It doesn’t seem that any std type would benefit from implementing it, and it doesn’t require compiler magic either.
I wanted an implementation which would be connected to the object itself ,so we wouldn't be handling two different variables, as it is it's initialization states, which is what Default Trait does.
Also a problem I ran into was unable to use enum to hold variable no of initial states as a trait, as the number of states could not be overriden in the inherited type, the main goal was to abstract away the function and make the implementation uniform across use cases
there was also an idea of using String based indexing as to be able to implement yet, by modifying the Slice into a Hashmap, but haven't gotten that far and running into some const issues(In the process of learning).
Enums Referenceing is the cleanest and easiest ,as for this it also allows for auto code completion , but not sure how to implement in trait , but combining the functionality of the two would be very cool
There is also the use case of having double indexes ,for two different identifiers for 2 different attributes while storing defaults ,which can't be expanded by using enum ,due to fixed variable names , but easily implemented using String based indexes in a Tuple. Hence why I thought of it in this way.
That seems fine then ,and it make sense to have it more data driven from files ,it's just I was using consts as a storage option ,will see what I can do (library ,etc) ,as per the small use case.