-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[reflection] cleanup derive_reflect
#2377
Conversation
9c4129e
to
e9187e3
Compare
derive_reflect
@@ -45,11 +40,47 @@ enum DeriveType { | |||
static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; | |||
static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; | |||
|
|||
fn active_fields(punctuated: &Punctuated<Field, Comma>) -> impl Iterator<Item = (&Field, usize)> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think naming the argument fields
would be clearer than punctuated
#[derive(Default)] | ||
struct PropAttributeArgs { | ||
pub ignore: Option<bool>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This struct might exist to group future attributes, but if no other attribute is already planned I agree with the simplification
}) | ||
.map(|(f, _attr, i)| (*f, *i)) | ||
.collect::<Vec<(&Field, usize)>>(); | ||
let active_fields = active_fields(&fields).collect::<Vec<_>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This merges two steps into one which is good, but I will probably need to undo it in #1395 in order to also get the list of inactive fields (see https://github.com/bevyengine/bevy/pull/1395/files#diff-786606f1541ef395577b541509d2834381796a7f9859e6aaeef68dcf551928bcR211-R220 ). That's my problem I guess :-)
@MrGVSV @jakobhellermann can I get a review on this from one of you? I think this is an easy win. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice and simple. Personally, I'd like more internal documentation on the added functions to help out future contributors, but it's not a blocking issue.
Because Nathan has been busy lately, I'm going to bias towards doing follow-up in future PRs :) |
bors r+ |
# Objective - The current `derive_reflect` implementation is a large function with difficult to follow code paths. - When determining the "active fields" (e.g. those that don't use `#[reflect(ignore)]`) the logic is unnecessarily complex. ## Solution - Refactor `derive_reflect` by breaking into into smaller functions, and simplifying the logic for obtaining the "active fields".
Build failed: |
@NathanSWard, any chance you'll have a chance to fix this up in the next week or so? If not, no worries. We'll open another PR off this and credit you. |
Is this PR made obsolete by #4712? |
Yeah that's my bad for not recognizing this prior work already. I'll definitely add a note to the PR description tonight. Thanks for pointing this out! |
# Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
# Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in bevyengine#2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
# Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in bevyengine#2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
…4140) # Objective Currently, `FromReflect` makes a couple assumptions: * Ignored fields must implement `Default` * Active fields must implement `FromReflect` * The reflected must be fully populated for active fields (can't use an empty `DynamicStruct`) However, one or both of these requirements might be unachievable, such as for external types. In these cases, it might be nice to tell `FromReflect` to use a custom default. ## Solution Added the `#[reflect(default)]` derive helper attribute. This attribute can be applied to any field (ignored or not) and will allow a default value to be specified in place of the regular `from_reflect()` call. It takes two forms: `#[reflect(default)]` and `#[reflect(default = "some_func")]`. The former specifies that `Default::default()` should be used while the latter specifies that `some_func()` should be used. This is pretty much [how serde does it](https://serde.rs/field-attrs.html#default). ### Example ```rust #[derive(Reflect, FromReflect)] struct MyStruct { // Use `Default::default()` #[reflect(default)] foo: String, // Use `get_bar_default()` #[reflect(default = "get_bar_default")] #[reflect(ignore)] bar: usize, } fn get_bar_default() -> usize { 123 } ``` ### Active Fields As an added benefit, this also allows active fields to be completely missing from their dynamic object. This is because the attribute tells `FromReflect` how to handle missing active fields (it still tries to use `from_reflect` first so the `FromReflect` trait is still required). ```rust let dyn_struct = DynamicStruct::default(); // We can do this without actually including the active fields since they have `#[reflect(default)]` let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` ### Container Defaults Also, with the addition of #3733, people will likely start adding `#[reflect(Default)]` to their types now. Just like with the fields, we can use this to mark the entire container as "defaultable". This grants us the ability to completely remove the field markers altogether if our type implements `Default` (and we're okay with fields using that instead of their own `Default` impls): ```rust #[derive(Reflect, FromReflect)] #[reflect(Default)] struct MyStruct { foo: String, #[reflect(ignore)] bar: usize, } impl Default for MyStruct { fn default() -> Self { Self { foo: String::from("Hello"), bar: 123, } } } // Again, we can now construct this from nothing pretty much let dyn_struct = DynamicStruct::default(); let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` Now if _any_ field is missing when using `FromReflect`, we simply fallback onto the container's `Default` implementation. This behavior can be completely overridden on a per-field basis, of course, by simply defining those same field attributes like before. ### Related * #3733 * #1395 * #2377 --- ## Changelog * Added `#[reflect(default)]` field attribute for `FromReflect` * Allows missing fields to be given a default value when using `FromReflect` * `#[reflect(default)]` - Use the field's `Default` implementation * `#[reflect(default = "some_fn")]` - Use a custom function to get the default value * Allow `#[reflect(Default)]` to have a secondary usage as a container attribute * Allows missing fields to be given a default value based on the container's `Default` impl when using `FromReflect` Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
…4140) # Objective Currently, `FromReflect` makes a couple assumptions: * Ignored fields must implement `Default` * Active fields must implement `FromReflect` * The reflected must be fully populated for active fields (can't use an empty `DynamicStruct`) However, one or both of these requirements might be unachievable, such as for external types. In these cases, it might be nice to tell `FromReflect` to use a custom default. ## Solution Added the `#[reflect(default)]` derive helper attribute. This attribute can be applied to any field (ignored or not) and will allow a default value to be specified in place of the regular `from_reflect()` call. It takes two forms: `#[reflect(default)]` and `#[reflect(default = "some_func")]`. The former specifies that `Default::default()` should be used while the latter specifies that `some_func()` should be used. This is pretty much [how serde does it](https://serde.rs/field-attrs.html#default). ### Example ```rust #[derive(Reflect, FromReflect)] struct MyStruct { // Use `Default::default()` #[reflect(default)] foo: String, // Use `get_bar_default()` #[reflect(default = "get_bar_default")] #[reflect(ignore)] bar: usize, } fn get_bar_default() -> usize { 123 } ``` ### Active Fields As an added benefit, this also allows active fields to be completely missing from their dynamic object. This is because the attribute tells `FromReflect` how to handle missing active fields (it still tries to use `from_reflect` first so the `FromReflect` trait is still required). ```rust let dyn_struct = DynamicStruct::default(); // We can do this without actually including the active fields since they have `#[reflect(default)]` let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` ### Container Defaults Also, with the addition of #3733, people will likely start adding `#[reflect(Default)]` to their types now. Just like with the fields, we can use this to mark the entire container as "defaultable". This grants us the ability to completely remove the field markers altogether if our type implements `Default` (and we're okay with fields using that instead of their own `Default` impls): ```rust #[derive(Reflect, FromReflect)] #[reflect(Default)] struct MyStruct { foo: String, #[reflect(ignore)] bar: usize, } impl Default for MyStruct { fn default() -> Self { Self { foo: String::from("Hello"), bar: 123, } } } // Again, we can now construct this from nothing pretty much let dyn_struct = DynamicStruct::default(); let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` Now if _any_ field is missing when using `FromReflect`, we simply fallback onto the container's `Default` implementation. This behavior can be completely overridden on a per-field basis, of course, by simply defining those same field attributes like before. ### Related * #3733 * #1395 * #2377 --- ## Changelog * Added `#[reflect(default)]` field attribute for `FromReflect` * Allows missing fields to be given a default value when using `FromReflect` * `#[reflect(default)]` - Use the field's `Default` implementation * `#[reflect(default = "some_fn")]` - Use a custom function to get the default value * Allow `#[reflect(Default)]` to have a secondary usage as a container attribute * Allows missing fields to be given a default value based on the container's `Default` impl when using `FromReflect` Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
…evyengine#4140) # Objective Currently, `FromReflect` makes a couple assumptions: * Ignored fields must implement `Default` * Active fields must implement `FromReflect` * The reflected must be fully populated for active fields (can't use an empty `DynamicStruct`) However, one or both of these requirements might be unachievable, such as for external types. In these cases, it might be nice to tell `FromReflect` to use a custom default. ## Solution Added the `#[reflect(default)]` derive helper attribute. This attribute can be applied to any field (ignored or not) and will allow a default value to be specified in place of the regular `from_reflect()` call. It takes two forms: `#[reflect(default)]` and `#[reflect(default = "some_func")]`. The former specifies that `Default::default()` should be used while the latter specifies that `some_func()` should be used. This is pretty much [how serde does it](https://serde.rs/field-attrs.html#default). ### Example ```rust #[derive(Reflect, FromReflect)] struct MyStruct { // Use `Default::default()` #[reflect(default)] foo: String, // Use `get_bar_default()` #[reflect(default = "get_bar_default")] #[reflect(ignore)] bar: usize, } fn get_bar_default() -> usize { 123 } ``` ### Active Fields As an added benefit, this also allows active fields to be completely missing from their dynamic object. This is because the attribute tells `FromReflect` how to handle missing active fields (it still tries to use `from_reflect` first so the `FromReflect` trait is still required). ```rust let dyn_struct = DynamicStruct::default(); // We can do this without actually including the active fields since they have `#[reflect(default)]` let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` ### Container Defaults Also, with the addition of bevyengine#3733, people will likely start adding `#[reflect(Default)]` to their types now. Just like with the fields, we can use this to mark the entire container as "defaultable". This grants us the ability to completely remove the field markers altogether if our type implements `Default` (and we're okay with fields using that instead of their own `Default` impls): ```rust #[derive(Reflect, FromReflect)] #[reflect(Default)] struct MyStruct { foo: String, #[reflect(ignore)] bar: usize, } impl Default for MyStruct { fn default() -> Self { Self { foo: String::from("Hello"), bar: 123, } } } // Again, we can now construct this from nothing pretty much let dyn_struct = DynamicStruct::default(); let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` Now if _any_ field is missing when using `FromReflect`, we simply fallback onto the container's `Default` implementation. This behavior can be completely overridden on a per-field basis, of course, by simply defining those same field attributes like before. ### Related * bevyengine#3733 * bevyengine#1395 * bevyengine#2377 --- ## Changelog * Added `#[reflect(default)]` field attribute for `FromReflect` * Allows missing fields to be given a default value when using `FromReflect` * `#[reflect(default)]` - Use the field's `Default` implementation * `#[reflect(default = "some_fn")]` - Use a custom function to get the default value * Allow `#[reflect(Default)]` to have a secondary usage as a container attribute * Allows missing fields to be given a default value based on the container's `Default` impl when using `FromReflect` Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
# Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in bevyengine#2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
…evyengine#4140) # Objective Currently, `FromReflect` makes a couple assumptions: * Ignored fields must implement `Default` * Active fields must implement `FromReflect` * The reflected must be fully populated for active fields (can't use an empty `DynamicStruct`) However, one or both of these requirements might be unachievable, such as for external types. In these cases, it might be nice to tell `FromReflect` to use a custom default. ## Solution Added the `#[reflect(default)]` derive helper attribute. This attribute can be applied to any field (ignored or not) and will allow a default value to be specified in place of the regular `from_reflect()` call. It takes two forms: `#[reflect(default)]` and `#[reflect(default = "some_func")]`. The former specifies that `Default::default()` should be used while the latter specifies that `some_func()` should be used. This is pretty much [how serde does it](https://serde.rs/field-attrs.html#default). ### Example ```rust #[derive(Reflect, FromReflect)] struct MyStruct { // Use `Default::default()` #[reflect(default)] foo: String, // Use `get_bar_default()` #[reflect(default = "get_bar_default")] #[reflect(ignore)] bar: usize, } fn get_bar_default() -> usize { 123 } ``` ### Active Fields As an added benefit, this also allows active fields to be completely missing from their dynamic object. This is because the attribute tells `FromReflect` how to handle missing active fields (it still tries to use `from_reflect` first so the `FromReflect` trait is still required). ```rust let dyn_struct = DynamicStruct::default(); // We can do this without actually including the active fields since they have `#[reflect(default)]` let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` ### Container Defaults Also, with the addition of bevyengine#3733, people will likely start adding `#[reflect(Default)]` to their types now. Just like with the fields, we can use this to mark the entire container as "defaultable". This grants us the ability to completely remove the field markers altogether if our type implements `Default` (and we're okay with fields using that instead of their own `Default` impls): ```rust #[derive(Reflect, FromReflect)] #[reflect(Default)] struct MyStruct { foo: String, #[reflect(ignore)] bar: usize, } impl Default for MyStruct { fn default() -> Self { Self { foo: String::from("Hello"), bar: 123, } } } // Again, we can now construct this from nothing pretty much let dyn_struct = DynamicStruct::default(); let my_struct = <MyStruct as FromReflect>::from_reflect(&dyn_struct); ``` Now if _any_ field is missing when using `FromReflect`, we simply fallback onto the container's `Default` implementation. This behavior can be completely overridden on a per-field basis, of course, by simply defining those same field attributes like before. ### Related * bevyengine#3733 * bevyengine#1395 * bevyengine#2377 --- ## Changelog * Added `#[reflect(default)]` field attribute for `FromReflect` * Allows missing fields to be given a default value when using `FromReflect` * `#[reflect(default)]` - Use the field's `Default` implementation * `#[reflect(default = "some_fn")]` - Use a custom function to get the default value * Allow `#[reflect(Default)]` to have a secondary usage as a container attribute * Allows missing fields to be given a default value based on the container's `Default` impl when using `FromReflect` Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Objective
derive_reflect
implementation is a large function with difficult to follow code paths.#[reflect(ignore)]
) the logic is unnecessarily complex.Solution
derive_reflect
by breaking into into smaller functions, and simplifying the logic for obtaining the "active fields".