5c7dd2eec3
55 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
7645ce91ed
|
Add newlines before impl blocks (#19746)
# Objective Fix https://github.com/bevyengine/bevy/issues/19617 # Solution Add newlines before all impl blocks. I suspect that at least some of these will be objectionable! If there's a desired Bevy style for this then I'll update the PR. If not then we can just close it - it's the work of a single find and replace. |
||
![]() |
3b25b38bdc
|
deny(missing_docs) for bevy_reflect (#19481)
## Objective Deny missing docs in bevy_reflect, towards https://github.com/bevyengine/bevy/issues/3492. ## Solution Add the missing docs! ## Testing N/A |
||
![]() |
a266e7e642
|
More uninlined_format_args fixes (#19396)
# Objective There are several uninlined format args (seems to be in more formatting macros and in more crates) that are not detected on stable, but are on nightly. ## Solution Fix them. |
||
![]() |
63e78fe489
|
Deprecated Begone! 0.16 Cleanup (#19108)
# Objective A fair few items were deprecated in 0.16. Let's delete them now that we're in the 0.17 development cycle! ## Solution - Deleted items marked deprecated in 0.16. ## Testing - CI --- ## Notes I'm making the assumption that _everything_ deprecated in 0.16 should be removed in 0.17. That may be a false assumption in certain cases. Please check the items to be removed to see if there are any exceptions we should keep around for another cycle! |
||
![]() |
c2854a2a05
|
bevy_reflect: Deprecate PartialReflect::clone_value (#18284)
# Objective #13432 added proper reflection-based cloning. This is a better method than cloning via `clone_value` for reasons detailed in the description of that PR. However, it may not be immediately apparent to users why one should be used over the other, and what the gotchas of `clone_value` are. ## Solution This PR marks `PartialReflect::clone_value` as deprecated, with the deprecation notice pointing users to `PartialReflect::reflect_clone`. However, it also suggests using a new method introduced in this PR: `PartialReflect::to_dynamic`. `PartialReflect::to_dynamic` is essentially a renaming of `PartialReflect::clone_value`. By naming it `to_dynamic`, we make it very obvious that what's returned is a dynamic type. The one caveat to this is that opaque types still use `reflect_clone` as they have no corresponding dynamic type. Along with changing the name, the method is now optional, and comes with a default implementation that calls out to the respective reflection subtrait method. This was done because there was really no reason to require manual implementors provide a method that almost always calls out to a known set of methods. Lastly, to make this default implementation work, this PR also did a similar thing with the `clone_dynamic ` methods on the reflection subtraits. For example, `Struct::clone_dynamic` has been marked deprecated and is superseded by `Struct::to_dynamic_struct`. This was necessary to avoid the "multiple names in scope" issue. ### Open Questions This PR maintains the original signature of `clone_value` on `to_dynamic`. That is, it takes `&self` and returns `Box<dyn PartialReflect>`. However, in order for this to work, it introduces a panic if the value is opaque and doesn't override the default `reflect_clone` implementation. One thing we could do to avoid the panic would be to make the conversion fallible, either returning `Option<Box<dyn PartialReflect>>` or `Result<Box<dyn PartialReflect>, ReflectCloneError>`. This makes using the method a little more involved (i.e. users have to either unwrap or handle the rare possibility of an error), but it would set us up for a world where opaque types don't strictly need to be `Clone`. Right now this bound is sort of implied by the fact that `clone_value` is a required trait method, and the default behavior of the macro is to use `Clone` for opaque types. Alternatively, we could keep the signature but make the method required. This maintains that implied bound where manual implementors must provide some way of cloning the value (or YOLO it and just panic), but also makes the API simpler to use. Finally, we could just leave it with the panic. It's unlikely this would occur in practice since our macro still requires `Clone` for opaque types, and thus this would only ever be an issue if someone were to manually implement `PartialReflect` without a valid `to_dynamic` or `reflect_clone` method. ## Testing You can test locally using the following command: ``` cargo test --package bevy_reflect --all-features ``` --- ## Migration Guide `PartialReflect::clone_value` is being deprecated. Instead, use `PartialReflect::to_dynamic` if wanting to create a new dynamic instance of the reflected value. Alternatively, use `PartialReflect::reflect_clone` to attempt to create a true clone of the underlying value. Similarly, the following methods have been deprecated and should be replaced with these alternatives: - `Array::clone_dynamic` → `Array::to_dynamic_array` - `Enum::clone_dynamic` → `Enum::to_dynamic_enum` - `List::clone_dynamic` → `List::to_dynamic_list` - `Map::clone_dynamic` → `Map::to_dynamic_map` - `Set::clone_dynamic` → `Set::to_dynamic_set` - `Struct::clone_dynamic` → `Struct::to_dynamic_struct` - `Tuple::clone_dynamic` → `Tuple::to_dynamic_tuple` - `TupleStruct::clone_dynamic` → `TupleStruct::to_dynamic_tuple_struct` |
||
![]() |
f5210c54d2
|
bevy_reflect: Reflection-based cloning (#13432)
# Objective Using `Reflect::clone_value` can be somewhat confusing to those unfamiliar with how Bevy's reflection crate works. For example take the following code: ```rust let value: usize = 123; let clone: Box<dyn Reflect> = value.clone_value(); ``` What can we expect to be the underlying type of `clone`? If you guessed `usize`, then you're correct! Let's try another: ```rust #[derive(Reflect, Clone)] struct Foo(usize); let value: Foo = Foo(123); let clone: Box<dyn Reflect> = value.clone_value(); ``` What about this code? What is the underlying type of `clone`? If you guessed `Foo`, unfortunately you'd be wrong. It's actually `DynamicStruct`. It's not obvious that the generated `Reflect` impl actually calls `Struct::clone_dynamic` under the hood, which always returns `DynamicStruct`. There are already some efforts to make this a bit more apparent to the end-user: #7207 changes the signature of `Reflect::clone_value` to instead return `Box<dyn PartialReflect>`, signaling that we're potentially returning a dynamic type. But why _can't_ we return `Foo`? `Foo` can obviously be cloned— in fact, we already derived `Clone` on it. But even without the derive, this seems like something `Reflect` should be able to handle. Almost all types that implement `Reflect` either contain no data (trivially clonable), they contain a `#[reflect_value]` type (which, by definition, must implement `Clone`), or they contain another `Reflect` type (which recursively fall into one of these three categories). This PR aims to enable true reflection-based cloning where you get back exactly the type that you think you do. ## Solution Add a `Reflect::reflect_clone` method which returns `Result<Box<dyn Reflect>, ReflectCloneError>`, where the `Box<dyn Reflect>` is guaranteed to be the same type as `Self`. ```rust #[derive(Reflect)] struct Foo(usize); let value: Foo = Foo(123); let clone: Box<dyn Reflect> = value.reflect_clone().unwrap(); assert!(clone.is::<Foo>()); ``` Notice that we didn't even need to derive `Clone` for this to work: it's entirely powered via reflection! Under the hood, the macro generates something like this: ```rust fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> { Ok(Box::new(Self { // The `reflect_clone` impl for `usize` just makes use of its `Clone` impl 0: Reflect::reflect_clone(&self.0)?.take().map_err(/* ... */)?, })) } ``` If we did derive `Clone`, we can tell `Reflect` to rely on that instead: ```rust #[derive(Reflect, Clone)] #[reflect(Clone)] struct Foo(usize); ``` <details> <summary>Generated Code</summary> ```rust fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> { Ok(Box::new(Clone::clone(self))) } ``` </details> Or, we can specify our own cloning function: ```rust #[derive(Reflect)] #[reflect(Clone(incremental_clone))] struct Foo(usize); fn incremental_clone(value: &usize) -> usize { *value + 1 } ``` <details> <summary>Generated Code</summary> ```rust fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> { Ok(Box::new(incremental_clone(self))) } ``` </details> Similarly, we can specify how fields should be cloned. This is important for fields that are `#[reflect(ignore)]`'d as we otherwise have no way to know how they should be cloned. ```rust #[derive(Reflect)] struct Foo { #[reflect(ignore, clone)] bar: usize, #[reflect(ignore, clone = "incremental_clone")] baz: usize, } fn incremental_clone(value: &usize) -> usize { *value + 1 } ``` <details> <summary>Generated Code</summary> ```rust fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> { Ok(Box::new(Self { bar: Clone::clone(&self.bar), baz: incremental_clone(&self.baz), })) } ``` </details> If we don't supply a `clone` attribute for an ignored field, then the method will automatically return `Err(ReflectCloneError::FieldNotClonable {/* ... */})`. `Err` values "bubble up" to the caller. So if `Foo` contains `Bar` and the `reflect_clone` method for `Bar` returns `Err`, then the `reflect_clone` method for `Foo` also returns `Err`. ### Attribute Syntax You might have noticed the differing syntax between the container attribute and the field attribute. This was purely done for consistency with the current attributes. There are PRs aimed at improving this. #7317 aims at making the "special-cased" attributes more in line with the field attributes syntactically. And #9323 aims at moving away from the stringified paths in favor of just raw function paths. ### Compatibility with Unique Reflect This PR was designed with Unique Reflect (#7207) in mind. This method actually wouldn't change that much (if at all) under Unique Reflect. It would still exist on `Reflect` and it would still `Option<Box<dyn Reflect>>`. In fact, Unique Reflect would only _improve_ the user's understanding of what this method returns. We may consider moving what's currently `Reflect::clone_value` to `PartialReflect` and possibly renaming it to `partial_reflect_clone` or `clone_dynamic` to better indicate how it differs from `reflect_clone`. ## Testing You can test locally by running the following command: ``` cargo test --package bevy_reflect ``` --- ## Changelog - Added `Reflect::reflect_clone` method - Added `ReflectCloneError` error enum - Added `#[reflect(Clone)]` container attribute - Added `#[reflect(clone)]` field attribute |
||
![]() |
1b7db895b7
|
Harden proc macro path resolution and add integration tests. (#17330)
This pr uses the `extern crate self as` trick to make proc macros behave the same way inside and outside bevy. # Objective - Removes noise introduced by `crate as` in the whole bevy repo. - Fixes #17004. - Hardens proc macro path resolution. ## TODO - [x] `BevyManifest` needs cleanup. - [x] Cleanup remaining `crate as`. - [x] Add proper integration tests to the ci. ## Notes - `cargo-manifest-proc-macros` is written by me and based/inspired by the old `BevyManifest` implementation and [`bkchr/proc-macro-crate`](https://github.com/bkchr/proc-macro-crate). - What do you think about the new integration test machinery I added to the `ci`? More and better integration tests can be added at a later stage. The goal of these integration tests is to simulate an actual separate crate that uses bevy. Ideally they would lightly touch all bevy crates. ## Testing - Needs RA test - Needs testing from other users - Others need to run at least `cargo run -p ci integration-test` and verify that they work. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
bf765e61b5
|
Add no_std support to bevy_reflect (#16256)
# Objective - Contributes to #15460 ## Solution - Added `std` feature (enabled by default) ## Testing - CI - `cargo check -p bevy_reflect --no-default-features --target "x86_64-unknown-none"` - UEFI demo application runs with this branch of `bevy_reflect`, allowing `derive(Reflect)` ## Notes - The [`spin`](https://crates.io/crates/spin) crate has been included to provide `RwLock` and `Once` (as an alternative to `OnceLock`) when the `std` feature is not enabled. Another alternative may be more desirable, please provide feedback if you have a strong opinion here! - Certain items (`Box`, `String`, `ToString`) provided by `alloc` have been added to `__macro_exports` as a way to avoid `alloc` vs `std` namespacing. I'm personally quite annoyed that we can't rely on `alloc` as a crate name in `std` environments within macros. I'd love an alternative to my approach here, but I suspect it's the least-bad option. - I would've liked to have an `alloc` feature (for allocation-free `bevy_reflect`), unfortunately, `erased_serde` unconditionally requires access to `Box`. Maybe one day we could design around this, but for now it just means `bevy_reflect` requires `alloc`. --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
afd0f1322d
|
Move all_tuples to a new crate (#16161)
# Objective Fixes #15941 ## Solution Created https://crates.io/crates/variadics_please and moved the code there; updating references `bevy_utils/macros` is deleted. ## Testing cargo check ## Migration Guide Use `variadics_please::{all_tuples, all_tuples_with_size}` instead of `bevy::utils::{all_tuples, all_tuples_with_size}`. |
||
![]() |
9f5f5d3d41
|
bevy_reflect: get_represented_kind_info APIs for reflected kinds (#14380)
# Objective Fixes #14378 --------- Signed-off-by: Torstein Grindvik <torstein.grindvik@muybridge.com> Co-authored-by: Torstein Grindvik <torstein.grindvik@muybridge.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
c841dd92a1
|
Documentation for variadics (#15387)
# Objective Relevant: #15208 ## Solution I went ahead and added the variadics documentation in all applicable locations. ## Testing - I built the documentation and inspected it to see whether the feature is there. |
||
![]() |
397f20e835
|
bevy_reflect: Generic parameter info (#15475)
# Objective Currently, reflecting a generic type provides no information about the generic parameters. This means that you can't get access to the type of `T` in `Foo<T>` without creating custom type data (we do this for [`ReflectHandle`](https://docs.rs/bevy/0.14.2/bevy/asset/struct.ReflectHandle.html#method.asset_type_id)). ## Solution This PR makes it so that generic type parameters and generic const parameters are tracked in a `Generics` struct stored on the `TypeInfo` for a type. For example, `struct Foo<T, const N: usize>` will store `T` and `N` as a `TypeParamInfo` and `ConstParamInfo`, respectively. The stored information includes: - The name of the generic parameter (i.e. `T`, `N`, etc.) - The type of the generic parameter (remember that we're dealing with monomorphized types, so this will actually be a concrete type) - The default type/value, if any (e.g. `f32` in `T = f32` or `10` in `const N: usize = 10`) ### Caveats The only requirement for this to work is that the user does not opt-out of the automatic `TypePath` derive with `#[reflect(type_path = false)]`. Doing so prevents the macro code from 100% knowing that the generic type implements `TypePath`. This in turn means the generated `Typed` impl can't add generics to the type. There are two solutions for this—both of which I think we should explore in a future PR: 1. We could just not use `TypePath`. This would mean that we can't store the `Type` of the generic, but we can at least store the `TypeId`. 2. We could provide a way to opt out of the automatic `Typed` derive with a `#[reflect(typed = false)]` attribute. This would allow users to manually implement `Typed` to add whatever generic information they need (e.g. skipping a parameter that can't implement `TypePath` while the rest can). I originally thought about making `Generics` an enum with `Generic`, `NonGeneric`, and `Unavailable` variants to signify whether there are generics, no generics, or generics that cannot be added due to opting out of `TypePath`. I ultimately decided against this as I think it adds a bit too much complexity for such an uncommon problem. Additionally, user's don't necessarily _have_ to know the generics of a type, so just skipping them should generally be fine for now. ## Testing You can test locally by running: ``` cargo test --package bevy_reflect ``` --- ## Showcase You can now access generic parameters via `TypeInfo`! ```rust #[derive(Reflect)] struct MyStruct<T, const N: usize>([T; N]); let generics = MyStruct::<f32, 10>::type_info().generics(); // Get by index: let t = generics.get(0).unwrap(); assert_eq!(t.name(), "T"); assert!(t.ty().is::<f32>()); assert!(!t.is_const()); // Or by name: let n = generics.get_named("N").unwrap(); assert_eq!(n.name(), "N"); assert!(n.ty().is::<usize>()); assert!(n.is_const()); ``` You can even access parameter defaults: ```rust #[derive(Reflect)] struct MyStruct<T = String, const N: usize = 10>([T; N]); let generics = MyStruct::<f32, 5>::type_info().generics(); let GenericInfo::Type(info) = generics.get_named("T").unwrap() else { panic!("expected a type parameter"); }; let default = info.default().unwrap(); assert!(default.is::<String>()); let GenericInfo::Const(info) = generics.get_named("N").unwrap() else { panic!("expected a const parameter"); }; let default = info.default().unwrap(); assert_eq!(default.downcast_ref::<usize>().unwrap(), &10); ``` |
||
![]() |
d70595b667
|
Add core and alloc over std Lints (#15281)
# Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
efda7f3f9c
|
Simpler lint fixes: makes ci lints work but disables a lint for now (#15376)
Takes the first two commits from #15375 and adds suggestions from this comment: https://github.com/bevyengine/bevy/pull/15375#issuecomment-2366968300 See #15375 for more reasoning/motivation. ## Rebasing (rerunning) ```rust git switch simpler-lint-fixes git reset --hard main cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "rustfmt" cargo clippy --workspace --all-targets --all-features --fix cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "clippy" git cherry-pick e6c0b94f6795222310fb812fa5c4512661fc7887 ``` |
||
![]() |
4d0961cc8a
|
bevy_reflect: Add ReflectRef /ReflectMut /ReflectOwned convenience casting methods (#15235)
# Objective #13320 added convenience methods for casting a `TypeInfo` into its respective variant: ```rust let info: &TypeInfo = <Vec<i32> as Typed>::type_info(); // We know `info` contains a `ListInfo`, so we can simply cast it: let list_info: &ListInfo = info.as_list().unwrap(); ``` This is especially helpful when you have already verified a type is a certain kind via `ReflectRef`, `ReflectMut`, `ReflectOwned`, or `ReflectKind`. As mentioned in that PR, though, it would be useful to add similar convenience methods to those types as well. ## Solution Added convenience casting methods to `ReflectRef`, `ReflectMut`, and `ReflectOwned`. With these methods, I was able to reduce our nesting in certain places throughout the crate. Additionally, I took this opportunity to move these types (and `ReflectKind`) to their own module to help clean up the `reflect` module. ## Testing You can test locally by running: ``` cargo test --package bevy_reflect --all-features ``` --- ## Showcase Convenience methods for casting `ReflectRef`, `ReflectMut`, and `ReflectOwned` into their respective variants has been added! This allows you to write cleaner code if you already know the kind of your reflected data: ```rust // BEFORE let ReflectRef::List(list) = list.reflect_ref() else { panic!("expected list"); }; // AFTER let list = list.reflect_ref().as_list().unwrap(); ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com> |
||
![]() |
3892adcb47
|
bevy_reflect: Add Type type (#14838)
# Objective Closes #7622. I was working on adding support for reflecting generic functions and found that I wanted to use an argument's `TypeId` for hashing and comparison, but its `TypePath` for debugging and error messaging. While I could just keep them separate, place them in a tuple or a local struct or something, I think I see an opportunity to make a dedicate type for this. Additionally, we can use this type to clean up some duplication amongst the type info structs in a manner similar to #7622. ## Solution Added the `Type` type. This should be seen as the most basic representation of a type apart from `TypeId`. It stores both the `TypeId` of the type as well as its `TypePathTable`. The `Hash` and `PartialEq` implementations rely on the `TypeId`, while the `Debug` implementation relies on the `TypePath`. This makes it especially useful as a key in a `HashMap` since we get the speed of the `TypeId` hashing/comparisons with the readability of `TypePath`. With this type, we're able to reduce the duplication across the type info structs by removing individual fields for `TypeId` and `TypePathTable`, replacing them with a single `Type` field. Similarly, we can remove many duplicate methods and replace it with a macro that delegates to the stored `Type`. ### Caveats It should be noted that this type is currently 3x larger than `TypeId`. On my machine, it's 48 bytes compared to `TypeId`'s 16. While this doesn't matter for `TypeInfo` since it would contain that data regardless, it is something to keep in mind when using elsewhere. ## Testing All tests should pass as normal: ``` cargo test --package bevy_reflect ``` --- ## Showcase `bevy_reflect` now exports a `Type` struct. This type contains both the `TypeId` and the `TypePathTable` of the given type, allowing it to be used like `TypeId` but have the debuggability of `TypePath`. ```rust // We can create this for any type implementing `TypePath`: let ty = Type::of::<String>(); // It has `Hash` and `Eq` impls powered by `TypeId`, making it useful for maps: let mut map = HashMap::<Type, i32>::new(); map.insert(ty, 25); // And it has a human-readable `Debug` representation: let debug = format!("{:?}", map); assert_eq!(debug, "{alloc::string::String: 25}"); ``` ## Migration Guide Certain type info structs now only return their item types as `Type` instead of exposing direct methods on them. The following methods have been removed: - `ArrayInfo::item_type_path_table` - `ArrayInfo::item_type_id` - `ArrayInfo::item_is` - `ListInfo::item_type_path_table` - `ListInfo::item_type_id` - `ListInfo::item_is` - `SetInfo::value_type_path_table` - `SetInfo::value_type_id` - `SetInfo::value_is` - `MapInfo::key_type_path_table` - `MapInfo::key_type_id` - `MapInfo::key_is` - `MapInfo::value_type_path_table` - `MapInfo::value_type_id` - `MapInfo::value_is` Instead, access the `Type` directly using one of the new methods: - `ArrayInfo::item_ty` - `ListInfo::item_ty` - `SetInfo::value_ty` - `MapInfo::key_ty` - `MapInfo::value_ty` For example: ```rust // BEFORE let type_id = array_info.item_type_id(); // AFTER let type_id = array_info.item_ty().id(); ``` |
||
![]() |
aab1f8e435
|
Use #[doc(fake_variadic)] to improve docs readability (#14703)
# Objective - Fixes #14697 ## Solution This PR modifies the existing `all_tuples!` macro to optionally accept a `#[doc(fake_variadic)]` attribute in its input. If the attribute is present, each invocation of the impl macro gets the correct attributes (i.e. the first impl receives `#[doc(fake_variadic)]` while the other impls are hidden using `#[doc(hidden)]`. Impls for the empty tuple (unit type) are left untouched (that's what the [standard library](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#impl-PartialEq-for-()) and [serde](https://docs.rs/serde/latest/serde/trait.Serialize.html#impl-Serialize-for-()) do). To work around https://github.com/rust-lang/cargo/issues/8811 and to get impls on re-exports to correctly show up as variadic, `--cfg docsrs_dep` is passed when building the docs for the toplevel `bevy` crate. `#[doc(fake_variadic)]` only works on tuples and fn pointers, so impls for structs like `AnyOf<(T1, T2, ..., Tn)>` are unchanged. ## Testing I built the docs locally using `RUSTDOCFLAGS='--cfg docsrs' RUSTFLAGS='--cfg docsrs_dep' cargo +nightly doc --no-deps --workspace` and checked the documentation page of a trait both in its original crate and the re-exported version in `bevy`. The description should correctly mention for how many tuple items the trait is implemented. I added `rustc-args` for docs.rs to the `bevy` crate, I hope there aren't any other notable crates that re-export `#[doc(fake_variadic)]` traits. --- ## Showcase `bevy_ecs::query::QueryData`: <img width="1015" alt="Screenshot 2024-08-12 at 16 41 28" src="https://github.com/user-attachments/assets/d40136ed-6731-475f-91a0-9df255cd24e3"> `bevy::ecs::query::QueryData` (re-export): <img width="1005" alt="Screenshot 2024-08-12 at 16 42 57" src="https://github.com/user-attachments/assets/71d44cf0-0ab0-48b0-9a51-5ce332594e12"> ## Original Description <details> Resolves #14697 Submitting as a draft for now, very WIP. Unfortunately, the docs don't show the variadics nicely when looking at reexported items. For example: `bevy_ecs::bundle::Bundle` correctly shows the variadic impl:  while `bevy::ecs::bundle::Bundle` (the reexport) shows all the impls (not good):  Built using `RUSTDOCFLAGS='--cfg docsrs' cargo +nightly doc --workspace --no-deps` (`--no-deps` because of wgpu-core). Maybe I missed something or this is a limitation in the *totally not private* `#[doc(fake_variadic)]` thingy. In any case I desperately need some sleep now :)) </details> |
||
![]() |
6ab8767d3b
|
reflect: implement the unique reflect rfc (#7207)
# Objective
- Implements the [Unique Reflect
RFC](https://github.com/nicopap/rfcs/blob/bevy-reflect-api/rfcs/56-better-reflect.md).
## Solution
- Implements the RFC.
- This implementation differs in some ways from the RFC:
- In the RFC, it was suggested `Reflect: Any` but `PartialReflect:
?Any`. During initial implementation I tried this, but we assume the
`PartialReflect: 'static` in a lot of places and the changes required
crept out of the scope of this PR.
- `PartialReflect::try_into_reflect` originally returned `Option<Box<dyn
Reflect>>` but i changed this to `Result<Box<dyn Reflect>, Box<dyn
PartialReflect>>` since the method takes by value and otherwise there
would be no way to recover the type. `as_full` and `as_full_mut` both
still return `Option<&(mut) dyn Reflect>`.
---
## Changelog
- Added `PartialReflect`.
- `Reflect` is now a subtrait of `PartialReflect`.
- Moved most methods on `Reflect` to the new `PartialReflect`.
- Added `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect}`.
- Added `PartialReflect::{try_as_reflect, try_as_reflect_mut,
try_into_reflect}`.
- Added `<dyn PartialReflect>::{try_downcast_ref, try_downcast_mut,
try_downcast, try_take}` supplementing the methods on `dyn Reflect`.
## Migration Guide
- Most instances of `dyn Reflect` should be changed to `dyn
PartialReflect` which is less restrictive, however trait bounds should
generally stay as `T: Reflect`.
- The new `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect, try_as_reflect, try_as_reflect_mut,
try_into_reflect}` methods as well as `Reflect::{as_reflect,
as_reflect_mut, into_reflect}` will need to be implemented for manual
implementors of `Reflect`.
## Future Work
- This PR is designed to be followed up by another "Unique Reflect Phase
2" that addresses the following points:
- Investigate making serialization revolve around `Reflect` instead of
`PartialReflect`.
- [Remove the `try_*` methods on `dyn PartialReflect` since they are
stop
gaps](https://github.com/bevyengine/bevy/pull/7207#discussion_r1083476050).
- Investigate usages like `ReflectComponent`. In the places they
currently use `PartialReflect`, should they be changed to use `Reflect`?
- Merging this opens the door to lots of reflection features we haven't
been able to implement.
- We could re-add [the `Reflectable`
trait](
|
||
![]() |
ab255aefc6
|
Implement FromIterator/IntoIterator for dynamic types (#14250)
# Objective Implement FromIterator/IntoIterator for dynamic types where missing Note: - can't impl `IntoIterator` for `&Array` & co because of orphan rules - `into_iter().collect()` is a no-op for `Vec`s because of specialization --- ## Migration Guide - Change `DynamicArray::from_vec` to `DynamicArray::from_iter` |
||
![]() |
aa241672e1
|
bevy_reflect: Nested TypeInfo getters (#13321)
# Objective Right now, `TypeInfo` can be accessed directly from a type using either `Typed::type_info` or `Reflect::get_represented_type_info`. However, once that `TypeInfo` is accessed, any nested types must be accessed via the `TypeRegistry`. ```rust #[derive(Reflect)] struct Foo { bar: usize } let registry = TypeRegistry::default(); let TypeInfo::Struct(type_info) = Foo::type_info() else { panic!("expected struct info"); }; let field = type_info.field("bar").unwrap(); let field_info = registry.get_type_info(field.type_id()).unwrap(); assert!(field_info.is::<usize>());; ``` ## Solution Enable nested types within a `TypeInfo` to be retrieved directly. ```rust #[derive(Reflect)] struct Foo { bar: usize } let TypeInfo::Struct(type_info) = Foo::type_info() else { panic!("expected struct info"); }; let field = type_info.field("bar").unwrap(); let field_info = field.type_info().unwrap(); assert!(field_info.is::<usize>());; ``` The particular implementation was chosen for two reasons. Firstly, we can't just store `TypeInfo` inside another `TypeInfo` directly. This is because some types are recursive and would result in a deadlock when trying to create the `TypeInfo` (i.e. it has to create the `TypeInfo` before it can use it, but it also needs the `TypeInfo` before it can create it). Therefore, we must instead store the function so it can be retrieved lazily. I had considered also using a `OnceLock` or something to lazily cache the info, but I figured we can look into optimizations later. The API should remain the same with or without the `OnceLock`. Secondly, a new wrapper trait had to be introduced: `MaybeTyped`. Like `RegisterForReflection`, this trait is `#[doc(hidden)]` and only exists so that we can properly handle dynamic type fields without requiring them to implement `Typed`. We don't want dynamic types to implement `Typed` due to the fact that it would make the return type `Option<&'static TypeInfo>` for all types even though only the dynamic types ever need to return `None` (see #6971 for details). Users should never have to interact with this trait as it has a blanket impl for all `Typed` types. And `Typed` is automatically implemented when deriving `Reflect` (as it is required). The one downside is we do need to return `Option<&'static TypeInfo>` from all these new methods so that we can handle the dynamic cases. If we didn't have to, we'd be able to get rid of the `Option` entirely. But I think that's an okay tradeoff for this one part of the API, and keeps the other APIs intact. ## Testing This PR contains tests to verify everything works as expected. You can test locally by running: ``` cargo test --package bevy_reflect ``` --- ## Changelog ### Public Changes - Added `ArrayInfo::item_info` method - Added `NamedField::type_info` method - Added `UnnamedField::type_info` method - Added `ListInfo::item_info` method - Added `MapInfo::key_info` method - Added `MapInfo::value_info` method - All active fields now have a `Typed` bound (remember that this is automatically satisfied for all types that derive `Reflect`) ### Internal Changes - Added `MaybeTyped` trait ## Migration Guide All active fields for reflected types (including lists, maps, tuples, etc.), must implement `Typed`. For the majority of users this won't have any visible impact. However, users implementing `Reflect` manually may need to update their types to implement `Typed` if they weren't already. Additionally, custom dynamic types will need to implement the new hidden `MaybeTyped` trait. |
||
![]() |
99c9218b56
|
bevy_reflect: Feature-gate function reflection (#14174)
# Objective Function reflection requires a lot of macro code generation in the form of several `all_tuples!` invocations, as well as impls generated in the `Reflect` derive macro. Seeing as function reflection is currently a bit more niche, it makes sense to gate it all behind a feature. ## Solution Add a `functions` feature to `bevy_reflect`, which can be enabled in Bevy using the `reflect_functions` feature. ## Testing You can test locally by running: ``` cargo test --package bevy_reflect ``` That should ensure that everything still works with the feature disabled. To test with the feature on, you can run: ``` cargo test --package bevy_reflect --features functions ``` --- ## Changelog - Moved function reflection behind a Cargo feature (`bevy/reflect_functions` and `bevy_reflect/functions`) - Add `IntoFunction` export in `bevy_reflect::prelude` ## Internal Migration Guide > [!important] > Function reflection was introduced as part of the 0.15 dev cycle. This migration guide was written for developers relying on `main` during this cycle, and is not a breaking change coming from 0.14. Function reflection is now gated behind a feature. To use function reflection, enable the feature: - If using `bevy_reflect` directly, enable the `functions` feature - If using `bevy`, enable the `reflect_functions` feature |
||
![]() |
276dd04001
|
bevy_reflect: Function reflection (#13152)
# Objective
We're able to reflect types sooooooo... why not functions?
The goal of this PR is to make functions callable within a dynamic
context, where type information is not readily available at compile
time.
For example, if we have a function:
```rust
fn add(left: i32, right: i32) -> i32 {
left + right
}
```
And two `Reflect` values we've already validated are `i32` types:
```rust
let left: Box<dyn Reflect> = Box::new(2_i32);
let right: Box<dyn Reflect> = Box::new(2_i32);
```
We should be able to call `add` with these values:
```rust
// ?????
let result: Box<dyn Reflect> = add.call_dynamic(left, right);
```
And ideally this wouldn't just work for functions, but methods and
closures too!
Right now, users have two options:
1. Manually parse the reflected data and call the function themselves
2. Rely on registered type data to handle the conversions for them
For a small function like `add`, this isn't too bad. But what about for
more complex functions? What about for many functions?
At worst, this process is error-prone. At best, it's simply tedious.
And this is assuming we know the function at compile time. What if we
want to accept a function dynamically and call it with our own
arguments?
It would be much nicer if `bevy_reflect` could alleviate some of the
problems here.
## Solution
Added function reflection!
This adds a `DynamicFunction` type to wrap a function dynamically. This
can be called with an `ArgList`, which is a dynamic list of
`Reflect`-containing `Arg` arguments. It returns a `FunctionResult`
which indicates whether or not the function call succeeded, returning a
`Reflect`-containing `Return` type if it did succeed.
Many functions can be converted into this `DynamicFunction` type thanks
to the `IntoFunction` trait.
Taking our previous `add` example, this might look something like
(explicit types added for readability):
```rust
fn add(left: i32, right: i32) -> i32 {
left + right
}
let mut function: DynamicFunction = add.into_function();
let args: ArgList = ArgList::new().push_owned(2_i32).push_owned(2_i32);
let result: Return = function.call(args).unwrap();
let value: Box<dyn Reflect> = result.unwrap_owned();
assert_eq!(value.take::<i32>().unwrap(), 4);
```
And it also works on closures:
```rust
let add = |left: i32, right: i32| left + right;
let mut function: DynamicFunction = add.into_function();
let args: ArgList = ArgList::new().push_owned(2_i32).push_owned(2_i32);
let result: Return = function.call(args).unwrap();
let value: Box<dyn Reflect> = result.unwrap_owned();
assert_eq!(value.take::<i32>().unwrap(), 4);
```
As well as methods:
```rust
#[derive(Reflect)]
struct Foo(i32);
impl Foo {
fn add(&mut self, value: i32) {
self.0 += value;
}
}
let mut foo = Foo(2);
let mut function: DynamicFunction = Foo::add.into_function();
let args: ArgList = ArgList::new().push_mut(&mut foo).push_owned(2_i32);
function.call(args).unwrap();
assert_eq!(foo.0, 4);
```
### Limitations
While this does cover many functions, it is far from a perfect system
and has quite a few limitations. Here are a few of the limitations when
using `IntoFunction`:
1. The lifetime of the return value is only tied to the lifetime of the
first argument (useful for methods). This means you can't have a
function like `(a: i32, b: &i32) -> &i32` without creating the
`DynamicFunction` manually.
2. Only 15 arguments are currently supported. If the first argument is a
(mutable) reference, this number increases to 16.
3. Manual implementations of `Reflect` will need to implement the new
`FromArg`, `GetOwnership`, and `IntoReturn` traits in order to be used
as arguments/return types.
And some limitations of `DynamicFunction` itself:
1. All arguments share the same lifetime, or rather, they will shrink to
the shortest lifetime.
2. Closures that capture their environment may need to have their
`DynamicFunction` dropped before accessing those variables again (there
is a `DynamicFunction::call_once` to make this a bit easier)
3. All arguments and return types must implement `Reflect`. While not a
big surprise coming from `bevy_reflect`, this implementation could
actually still work by swapping `Reflect` out with `Any`. Of course,
that makes working with the arguments and return values a bit harder.
4. Generic functions are not supported (unless they have been manually
monomorphized)
And general, reflection gotchas:
1. `&str` does not implement `Reflect`. Rather, `&'static str`
implements `Reflect` (the same is true for `&Path` and similar types).
This means that `&'static str` is considered an "owned" value for the
sake of generating arguments. Additionally, arguments and return types
containing `&str` will assume it's `&'static str`, which is almost never
the desired behavior. In these cases, the only solution (I believe) is
to use `&String` instead.
### Followup Work
This PR is the first of two PRs I intend to work on. The second PR will
aim to integrate this new function reflection system into the existing
reflection traits and `TypeInfo`. The goal would be to register and call
a reflected type's methods dynamically.
I chose not to do that in this PR since the diff is already quite large.
I also want the discussion for both PRs to be focused on their own
implementation.
Another followup I'd like to do is investigate allowing common container
types as a return type, such as `Option<&[mut] T>` and `Result<&[mut] T,
E>`. This would allow even more functions to opt into this system. I
chose to not include it in this one, though, for the same reasoning as
previously mentioned.
### Alternatives
One alternative I had considered was adding a macro to convert any
function into a reflection-based counterpart. The idea would be that a
struct that wraps the function would be created and users could specify
which arguments and return values should be `Reflect`. It could then be
called via a new `Function` trait.
I think that could still work, but it will be a fair bit more involved,
requiring some slightly more complex parsing. And it of course is a bit
more work for the user, since they need to create the type via macro
invocation.
It also makes registering these functions onto a type a bit more
complicated (depending on how it's implemented).
For now, I think this is a fairly simple, yet powerful solution that
provides the least amount of friction for users.
---
## Showcase
Bevy now adds support for storing and calling functions dynamically
using reflection!
```rust
// 1. Take a standard Rust function
fn add(left: i32, right: i32) -> i32 {
left + right
}
// 2. Convert it into a type-erased `DynamicFunction` using the `IntoFunction` trait
let mut function: DynamicFunction = add.into_function();
// 3. Define your arguments from reflected values
let args: ArgList = ArgList::new().push_owned(2_i32).push_owned(2_i32);
// 4. Call the function with your arguments
let result: Return = function.call(args).unwrap();
// 5. Extract the return value
let value: Box<dyn Reflect> = result.unwrap_owned();
assert_eq!(value.take::<i32>().unwrap(), 4);
```
## Changelog
#### TL;DR
- Added support for function reflection
- Added a new `Function Reflection` example:
|
||
![]() |
278380394f
|
Avoid bevy_reflect::List::iter wrapping in release mode (#13271)
# Objective Fixes #13230 ## Solution Uses solution described in #13230 They mention a worry about adding a branch, but I'm not sure there is one. This code ```Rust #[no_mangle] pub fn next_if_some(num: i32, b: Option<bool>) -> i32 { num + b.is_some() as i32 } ``` produces this assembly with opt-level 3 ```asm next_if_some: xor eax, eax cmp sil, 2 setne al add eax, edi ret ``` ## Testing Added test from #13230, tagged it as ignore as it is only useful in release mode and very slow if you accidentally invoke it in debug mode. --- ## Changelog Iterationg of ListIter will no longer overflow and wrap around ## Migration Guide |
||
![]() |
9c4ac7c297
|
Finish the work on try_apply (#12646)
# Objective Finish the `try_apply` implementation started in #6770 by @feyokorenhof. Supersedes and closes #6770. Closes #6182 ## Solution Add `try_apply` to `Reflect` and implement it in all the places that implement `Reflect`. --- ## Changelog Added `try_apply` to `Reflect`. --------- Co-authored-by: Feyo Korenhof <feyokorenhof@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
![]() |
ccb9d0500f
|
bevy_reflect: Recursive registration (#5781)
# Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
054134fba2
|
Add ReflectKind (#11664)
# Objective Fix https://github.com/bevyengine/bevy/issues/11657 ## Solution Add a `ReflectKind` enum, add `Reflect::reflect_kind` which returns a `ReflectKind`, and add `kind` method implementions to `ReflectRef`, `ReflectMut`, and `ReflectOwned`, which returns a `ReflectKind`. I also changed `AccessError` to use this new struct instead of it's own `TypeKind` struct. --- ## Changelog - Added `ReflectKind`, an enumeration over the kinds of a reflected type without its data. - Added `Reflect::reflect_kind` (with default implementation) - Added implementation for the `kind` method on `ReflectRef`, `ReflectMut`, and `ReflectOwned` which gives their kind without any information, as a `ReflectKind` |
||
![]() |
1568d4a415
|
Reorder impl to be the same as the trait (#11076)
# Objective - Make the implementation order consistent between all sources to fit the order in the trait. ## Solution - Change the implementation order. |
||
![]() |
fd308571c4
|
Remove unnecessary path prefixes (#10749)
# Objective - Shorten paths by removing unnecessary prefixes ## Solution - Remove the prefixes from many paths which do not need them. Finding the paths was done automatically using built-in refactoring tools in Jetbrains RustRover. |
||
![]() |
262846e702
|
reflect: TypePath part 2 (#8768)
# Objective
- Followup to #7184.
- ~Deprecate `TypeUuid` and remove its internal references.~ No longer
part of this PR.
- Use `TypePath` for the type registry, and (de)serialisation instead of
`std::any::type_name`.
- Allow accessing type path information behind proxies.
## Solution
- Introduce methods on `TypeInfo` and friends for dynamically querying
type path. These methods supersede the old `type_name` methods.
- Remove `Reflect::type_name` in favor of `DynamicTypePath::type_path`
and `TypeInfo::type_path_table`.
- Switch all uses of `std::any::type_name` in reflection, non-debugging
contexts to use `TypePath`.
---
## Changelog
- Added `TypePathTable` for dynamically accessing methods on `TypePath`
through `TypeInfo` and the type registry.
- Removed `type_name` from all `TypeInfo`-like structs.
- Added `type_path` and `type_path_table` methods to all `TypeInfo`-like
structs.
- Removed `Reflect::type_name` in favor of
`DynamicTypePath::reflect_type_path` and `TypeInfo::type_path`.
- Changed the signature of all `DynamicTypePath` methods to return
strings with a static lifetime.
## Migration Guide
- Rely on `TypePath` instead of `std::any::type_name` for all stability
guarantees and for use in all reflection contexts, this is used through
with one of the following APIs:
- `TypePath::type_path` if you have a concrete type and not a value.
- `DynamicTypePath::reflect_type_path` if you have an `dyn Reflect`
value without a concrete type.
- `TypeInfo::type_path` for use through the registry or if you want to
work with the represented type of a `DynamicFoo`.
- Remove `type_name` from manual `Reflect` implementations.
- Use `type_path` and `type_path_table` in place of `type_name` on
`TypeInfo`-like structs.
- Use `get_with_type_path(_mut)` over `get_with_type_name(_mut)`.
## Note to reviewers
I think if anything we were a little overzealous in merging #7184 and we
should take that extra care here.
In my mind, this is the "point of no return" for `TypePath` and while I
think we all agree on the design, we should carefully consider if the
finer details and current implementations are actually how we want them
moving forward.
For example [this incorrect `TypePath` implementation for
`String`](
|
||
![]() |
8b9d88f4d0
|
Reflect now requires DynamicTypePath. Remove Reflect::get_type_path() (#8764)
Followup to #7184 This makes `Reflect: DynamicTypePath` which allows us to remove `Reflect::get_type_path`, reducing unnecessary codegen and simplifying `Reflect` implementations. |
||
![]() |
1efc762924
|
reflect: stable type path v2 (#7184)
# Objective
- Introduce a stable alternative to
[`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html).
- Rewrite of #5805 with heavy inspiration in design.
- On the path to #5830.
- Part of solving #3327.
## Solution
- Add a `TypePath` trait for static stable type path/name information.
- Add a `TypePath` derive macro.
- Add a `impl_type_path` macro for implementing internal and foreign
types in `bevy_reflect`.
---
## Changelog
- Added `TypePath` trait.
- Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`.
- Added a `TypePath` derive macro.
- Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on
internal and foreign types in `bevy_reflect`.
- Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to
`(Non)GenericTypedCell<T>` which allows us to be generic over both
`TypeInfo` and `TypePath`.
- `TypePath` is now a supertrait of `Asset`, `Material` and
`Material2d`.
- `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be
specified.
- `impl_reflect_value` needs to either specify path starting with a
double colon (`::core::option::Option`) or an `in my_crate::foo`
declaration.
- Added `bevy_reflect_derive::ReflectTypePath`.
- Most uses of `Ident` in `bevy_reflect_derive` changed to use
`ReflectTypePath`.
## Migration Guide
- Implementors of `Asset`, `Material` and `Material2d` now also need to
derive `TypePath`.
- Manual implementors of `Reflect` will need to implement the new
`get_type_path` method.
## Open Questions
- [x] ~This PR currently does not migrate any usages of
`std::any::type_name` to use `bevy_reflect::TypePath` to ease the review
process. Should it?~ Migration will be left to a follow-up PR.
- [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to
satisfy new bounds, mostly when deriving `TypeUuid`. Should we make
`TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in
favour of
`TypePath`?](
|
||
![]() |
75130bd5ec
|
bevy_reflect: Better proxies (#6971)
# Objective > This PR is based on discussion from #6601 The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as both: 1. Dynamic containers which may hold any arbitrary data 2. Proxy types which may represent any other type Currently, the only way we can represent the proxy-ness of a Dynamic is by giving it a name. ```rust // This is just a dynamic container let mut data = DynamicStruct::default(); // This is a "proxy" data.set_name(std::any::type_name::<Foo>()); ``` This type name is the only way we check that the given Dynamic is a proxy of some other type. When we need to "assert the type" of a `dyn Reflect`, we call `Reflect::type_name` on it. However, because we're only using a string to denote the type, we run into a few gotchas and limitations. For example, hashing a Dynamic proxy may work differently than the type it proxies: ```rust #[derive(Reflect, Hash)] #[reflect(Hash)] struct Foo(i32); let concrete = Foo(123); let dynamic = concrete.clone_dynamic(); let concrete_hash = concrete.reflect_hash(); let dynamic_hash = dynamic.reflect_hash(); // The hashes are not equal because `concrete` uses its own `Hash` impl // while `dynamic` uses a reflection-based hashing algorithm assert_ne!(concrete_hash, dynamic_hash); ``` Because the Dynamic proxy only knows about the name of the type, it's unaware of any other information about it. This means it also differs on `Reflect::reflect_partial_eq`, and may include ignored or skipped fields in places the concrete type wouldn't. ## Solution Rather than having Dynamics pass along just the type name of proxied types, we can instead have them pass around the `TypeInfo`. Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than a `String`: ```diff pub struct DynamicTupleStruct { - type_name: String, + represented_type: Option<&'static TypeInfo>, fields: Vec<Box<dyn Reflect>>, } ``` By changing `Reflect::get_type_info` to `Reflect::represented_type_info`, hopefully we make this behavior a little clearer. And to account for `None` values on these dynamic types, `Reflect::represented_type_info` now returns `Option<&'static TypeInfo>`. ```rust let mut data = DynamicTupleStruct::default(); // Not proxying any specific type assert!(dyn_tuple_struct.represented_type_info().is_none()); let type_info = <Foo as Typed>::type_info(); dyn_tuple_struct.set_represented_type(Some(type_info)); // Alternatively: // let dyn_tuple_struct = foo.clone_dynamic(); // Now we're proxying `Foo` assert!(dyn_tuple_struct.represented_type_info().is_some()); ``` This means that we can have full access to all the static type information for the proxied type. Future work would include transitioning more static type information (trait impls, attributes, etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic proxies. ### Alternatives & Rationale > **Note** > These alternatives were written when this PR was first made using a `Proxy` trait. This trait has since been removed. <details> <summary>View</summary> #### Alternative: The `Proxy<T>` Approach I had considered adding something like a `Proxy<T>` type where `T` would be the Dynamic and would contain the proxied type information. This was nice in that it allows us to explicitly determine whether something is a proxy or not at a type level. `Proxy<DynamicStruct>` proxies a struct. Makes sense. The reason I didn't go with this approach is because (1) tuples, (2) complexity, and (3) `PartialReflect`. The `DynamicTuple` struct allows us to represent tuples at runtime. It also allows us to do something you normally can't with tuples: add new fields. Because of this, adding a field immediately invalidates the proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32, NewField)`). By going with this PR's approach, we can just remove the type info on `DynamicTuple` when that happens. However, with the `Proxy<T>` approach, it becomes difficult to represent this behavior— we'd have to completely control how we access data for `T` for each `T`. Secondly, it introduces some added complexities (aside from the manual impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we want to represent it as `dyn Reflect`. What `TypeInfo` do we give it? How would we forward reflection methods to the inner type (remember, we don't have specialization)? How do we separate this from Dynamic types? And finally, how do all this in a way that's both logical and intuitive for users? Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is actually more inline with the [Unique Reflect RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy` trait is really one part of the `PartialReflect` trait introduced in that RFC (it's technically not in that RFC but it fits well with it), where the `PartialReflect` serves as a way for proxies to work _like_ concrete types without having full access to everything a concrete `Reflect` type can do. This would help bridge the gap between the current state of the crate and the implementation of that RFC. All that said, this is still a viable solution. If the community believes this is the better path forward, then we can do that instead. These were just my reasons for not initially going with it in this PR. #### Alternative: The Type Registry Approach The `Proxy` trait is great and all, but how does it solve the original problem? Well, it doesn't— yet! The goal would be to start moving information from the derive macro and its attributes to the generated `TypeInfo` since these are known statically and shouldn't change. For example, adding `ignored: bool` to `[Un]NamedField` or a list of impls. However, there is another way of storing this information. This is, of course, one of the uses of the `TypeRegistry`. If we're worried about Dynamic proxies not aligning with their concrete counterparts, we could move more type information to the registry and require its usage. For example, we could replace `Reflect::reflect_hash(&self)` with `Reflect::reflect_hash(&self, registry: &TypeRegistry)`. That's not the _worst_ thing in the world, but it is an ergonomics loss. Additionally, other attributes may have their own requirements, further restricting what's possible without the registry. The `Reflect::apply` method will require the registry as well now. Why? Well because the `map_apply` function used for the `Reflect::apply` impls on `Map` types depends on `Map::insert_boxed`, which (at least for `DynamicMap`) requires `Reflect::reflect_hash`. The same would apply when adding support for reflection-based diffing, which will require `Reflect::reflect_partial_eq`. Again, this is a totally viable alternative. I just chose not to go with it for the reasons above. If we want to go with it, then we can close this PR and we can pursue this alternative instead. #### Downsides Just to highlight a quick potential downside (likely needs more investigation): retrieving the `TypeInfo` requires acquiring a lock on the `GenericTypeInfoCell` used by the `Typed` impls for generic types (non-generic types use a `OnceBox which should be faster). I am not sure how much of a performance hit that is and will need to run some benchmarks to compare against. </details> ### Open Questions 1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be easier for modding? Perhaps, in that case, we need to update `Typed::type_info` and friends as well? 2. Are the alternatives better than the approach this PR takes? Are there other alternatives? --- ## Changelog ### Changed - `Reflect::get_type_info` has been renamed to `Reflect::represented_type_info` - This method now returns `Option<&'static TypeInfo>` rather than just `&'static TypeInfo` ### Added - Added `Reflect::is_dynamic` method to indicate when a type is dynamic - Added a `set_represented_type` method on all dynamic types ### Removed - Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead) - Removed `Typed` impls for all dynamic types ## Migration Guide - The Dynamic types no longer take a string type name. Instead, they require a static reference to `TypeInfo`: ```rust #[derive(Reflect)] struct MyTupleStruct(f32, f32); let mut dyn_tuple_struct = DynamicTupleStruct::default(); dyn_tuple_struct.insert(1.23_f32); dyn_tuple_struct.insert(3.21_f32); // BEFORE: let type_name = std::any::type_name::<MyTupleStruct>(); dyn_tuple_struct.set_name(type_name); // AFTER: let type_info = <MyTupleStruct as Typed>::type_info(); dyn_tuple_struct.set_represented_type(Some(type_info)); ``` - `Reflect::get_type_info` has been renamed to `Reflect::represented_type_info` and now also returns an `Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`): ```rust // BEFORE: let info: &'static TypeInfo = value.get_type_info(); // AFTER: let info: &'static TypeInfo = value.represented_type_info().unwrap(); ``` - `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use `Reflect::is_dynamic` instead: ```rust // BEFORE: if matches!(value.get_type_info(), TypeInfo::Dynamic) { // ... } // AFTER: if value.is_dynamic() { // ... } ``` --------- Co-authored-by: radiish <cb.setho@gmail.com> |
||
![]() |
cd1737ecca |
bevy_reflect: Improved documentation (#7148)
# Objective
`bevy_reflect` can be a moderately complex crate to try and understand. It has many moving parts, a handful of gotchas, and a few subtle contracts that aren't immediately obvious to users and even other contributors.
The current README does an okay job demonstrating how the crate can be used. However, the crate's actual documentation should give a better overview of the crate, its inner-workings, and show some of its own examples.
## Solution
Added crate-level documentation that attempts to summarize the main parts of `bevy_reflect` into small sections.
This PR also updates the documentation for:
- `Reflect`
- `FromReflect`
- The reflection subtraits
- Other important types and traits
- The reflection macros (including the derive macros)
- Crate features
### Open Questions
1. ~~Should I update the docs for the Dynamic types? I was originally going to, but I'm getting a little concerned about the size of this PR 😅~~ Decided to not do this in this PR. It'll be better served from its own PR.
2. Should derive macro documentation be moved to the trait itself? This could improve visibility and allow for better doc links, but could also clutter up the trait's documentation (as well as not being on the actual derive macro's documentation).
### TODO
- [ ] ~~Document Dynamic types (?)~~ I think this should be done in a separate PR.
- [x] Document crate features
- [x] Update docs for `GetTypeRegistration`
- [x] Update docs for `TypeRegistration`
- [x] Update docs for `derive_from_reflect`
- [x] Document `reflect_trait`
- [x] Document `impl_reflect_value`
- [x] Document `impl_from_reflect_value`
---
## Changelog
- Updated documentation across the `bevy_reflect` crate
- Removed `#[module]` helper attribute for `Reflect` derives (this is not currently used)
## Migration Guide
- Removed `#[module]` helper attribute for `Reflect` derives. If your code is relying on this attribute, please replace it with either `#[reflect]` or `#[reflect_value]` (dependent on use-case).
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
|
||
![]() |
02fbf16c80 |
bevy_reflect: Add Reflect::into_reflect (#6502)
# Objective Using `Reflect` we can easily switch between a specific reflection trait object, such as a `dyn Struct`, to a `dyn Reflect` object via `Reflect::as_reflect` or `Reflect::as_reflect_mut`. ```rust fn do_something(value: &dyn Reflect) {/* ... */} let foo: Box<dyn Struct> = Box::new(Foo::default()); do_something(foo.as_reflect()); ``` However, there is no way to convert a _boxed_ reflection trait object to a `Box<dyn Reflect>`. ## Solution Add a `Reflect::into_reflect` method which allows converting a boxed reflection trait object back into a boxed `Reflect` trait object. ```rust fn do_something(value: Box<dyn Reflect>) {/* ... */} let foo: Box<dyn Struct> = Box::new(Foo::default()); do_something(foo.into_reflect()); ``` --- ## Changelog - Added `Reflect::into_reflect` |
||
![]() |
feebbc5ea9 |
Add reflect_owned (#6494)
# Objective There is no way to gen an owned value of `Reflect`. ## Solution Add it! This was originally a part of #6421, but @MrGVSV asked me to create a separate for it to implement reflect diffing. --- ## Changelog ### Added - `Reflect::reflect_owned` to get an owned version of `Reflect`. |
||
![]() |
e5905379de |
Use new let-else syntax where possible (#6463)
# Objective Let-else syntax is now stable! ## Solution Use it where possible! |
||
![]() |
a658bfef19 |
bevy_reflect: Reflect doc comments (#6234)
# Objective Resolves #6197 Make it so that doc comments can be retrieved via reflection. ## Solution Adds the new `documentation` feature to `bevy_reflect` (disabled by default). When enabled, documentation can be found using `TypeInfo::doc` for reflected types: ```rust /// Some struct. /// /// # Example /// /// ```ignore /// let some_struct = SomeStruct; /// ``` #[derive(Reflect)] struct SomeStruct; let info = <SomeStruct as Typed>::type_info(); assert_eq!( Some(" Some struct.\n\n # Example\n\n ```ignore\n let some_struct = SomeStruct;\n ```"), info.docs() ); ``` ### Notes for Reviewers The bulk of the files simply added the same 16 lines of code (with slightly different documentation). Most of the real changes occur in the `bevy_reflect_derive` files as well as in the added tests. --- ## Changelog * Added `documentation` feature to `bevy_reflect` * Added `TypeInfo::docs` method (and similar methods for all info types) |
||
![]() |
ac1aebed5e |
Add reflect(skip_serializing) which retains reflection but disables automatic serialization (#5250)
# Objective - To address problems outlined in https://github.com/bevyengine/bevy/issues/5245 ## Solution - Introduce `reflect(skip_serializing)` on top of `reflect(ignore)` which disables automatic serialisation to scenes, but does not disable reflection of the field. --- ## Changelog - Adds: - `bevy_reflect::serde::type_data` module - `SerializationData` structure for describing which fields are to be/not to be ignored, automatically registers as type_data for struct-based types - the `skip_serialization` flag for `#[reflect(...)]` - Removes: - ability to ignore Enum variants in serialization, since that didn't work anyway ## Migration Guide - Change `#[reflect(ignore)]` to `#[reflect(skip_serializing)]` where disabling reflection is not the intended effect. - Remove ignore/skip attributes from enum variants as these won't do anything anymore |
||
![]() |
ecc584ff23 |
bevy_reflect: Get owned fields (#5728)
# Objective Sometimes it's useful to be able to retrieve all the fields of a container type so that they may be processed separately. With reflection, however, we typically only have access to references. The only alternative is to "clone" the value using `Reflect::clone_value`. This, however, returns a Dynamic type in most cases. The solution there would be to use `FromReflect` instead, but this also has a problem in that it means we need to add `FromReflect` as an additional bound. ## Solution Add a `drain` method to all container traits. This returns a `Vec<Box<dyn Reflect>>` (except for `Map` which returns `Vec<(Box<dyn Reflect>, Box<dyn Reflect>)>`). This allows us to do things a lot simpler. For example, if we finished processing a struct and just need a particular value: ```rust // === OLD === // /// May or may not return a Dynamic*** value (even if `container` wasn't a `DynamicStruct`) fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.field_at(output_index).unwrap().clone_value() } // === NEW === // /// Returns _exactly_ whatever was in the given struct fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.drain().remove(output_index).unwrap() } ``` ### Discussion * Is `drain` the best method name? It makes sense that it "drains" all the fields and that it consumes the container in the process, but I'm open to alternatives. --- ## Changelog * Added a `drain` method to the following traits: * `Struct` * `TupleStruct` * `Tuple` * `Array` * `List` * `Map` * `Enum` |
||
![]() |
4b191d968d |
remove blanket Serialize + Deserialize requirement for Reflect on generic types (#5197)
# Objective Some generic types like `Option<T>`, `Vec<T>` and `HashMap<K, V>` implement `Reflect` when where their generic types `T`/`K`/`V` implement `Serialize + for<'de> Deserialize<'de>`. This is so that in their `GetTypeRegistration` impl they can insert the `ReflectSerialize` and `ReflectDeserialize` type data structs. This has the annoying side effect that if your struct contains a `Option<NonSerdeStruct>` you won't be able to derive reflect (https://github.com/bevyengine/bevy/issues/4054). ## Solution - remove the `Serialize + Deserialize` bounds on wrapper types - this means that `ReflectSerialize` and `ReflectDeserialize` will no longer be inserted even for `.register::<Option<DoesImplSerde>>()` - add `register_type_data<T, D>` shorthand for `registry.get_mut(T).insert(D::from_type<T>())` - require users to register their specific generic types **and the serde types** separately like ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` I believe this is the best we can do for extensibility and convenience without specialization. ## Changelog - `.register_type` for generic types like `Option<T>`, `Vec<T>`, `HashMap<K, V>` will no longer insert `ReflectSerialize` and `ReflectDeserialize` type data. Instead you need to register it separately for concrete generic types like so: ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` TODO: more docs and tweaks to the scene example to demonstrate registering generic types. |
||
![]() |
40982cd0a2 |
Make reflect_partial_eq return more accurate results (#5210)
# Objective Closes #5204 ## Solution - Followed @nicopap suggestion on https://github.com/bevyengine/bevy/pull/4761#discussion_r903982224 ## Changelog - [x] Updated [struct_trait]( |
||
![]() |
c27a3cff6d |
Make Reflect safe to implement (#5010)
# Objective Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract. ## Solution This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`. `any` now takes a `Box<Self>` and returns a `Box<dyn Any>`. --- ## Changelog ### Added: - `any()` method - `represents()` method ### Changed: - `Reflect` is now a safe trait - `downcast()` is now safe - The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any` ## Migration Guide - Reflect derives should not have to change anything - Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`. - Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any` ## Points of discussion: - Should renaming `any` be avoided and instead name the new method `any_box`? - ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~ - ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~ Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com> |
||
![]() |
e6f34ba47f |
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField:🆕:<T>()]; let info = TupleStructInfo:🆕:<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR ( |
||
![]() |
2f5591ff8c |
bevy_reflect: Improve debug formatting for reflected types (#4218)
# Objective Debugging reflected types can be somewhat frustrating since all `dyn Reflect` trait objects return something like `Reflect(core::option::Option<alloc::string::String>)`. It would be much nicer to be able to see the actual value— or even use a custom `Debug` implementation. ## Solution Added `Reflect::debug` which allows users to customize the debug output. It sets defaults for all `ReflectRef` subtraits and falls back to `Reflect(type_name)` if no `Debug` implementation was registered. To register a custom `Debug` impl, users can add `#[reflect(Debug)]` like they can with other traits. ### Example Using the following structs: ```rust #[derive(Reflect)] pub struct Foo { a: usize, nested: Bar, #[reflect(ignore)] _ignored: NonReflectedValue, } #[derive(Reflect)] pub struct Bar { value: Vec2, tuple_value: (i32, String), list_value: Vec<usize>, // We can't determine debug formatting for Option<T> yet unknown_value: Option<String>, custom_debug: CustomDebug } #[derive(Reflect)] #[reflect(Debug)] struct CustomDebug; impl Debug for CustomDebug { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "This is a custom debug!") } } pub struct NonReflectedValue { _a: usize, } ``` We can do: ```rust let value = Foo { a: 1, _ignored: NonReflectedValue { _a: 10 }, nested: Bar { value: Vec2::new(1.23, 3.21), tuple_value: (123, String::from("Hello")), list_value: vec![1, 2, 3], unknown_value: Some(String::from("World")), custom_debug: CustomDebug }, }; let reflected_value: &dyn Reflect = &value; println!("{:#?}", reflected_value) ``` Which results in: ```rust Foo { a: 2, nested: Bar { value: Vec2( 1.23, 3.21, ), tuple_value: ( 123, "Hello", ), list_value: [ 1, 2, 3, ], unknown_value: Reflect(core::option::Option<alloc::string::String>), custom_debug: This is a custom debug!, }, } ``` Notice that neither `Foo` nor `Bar` implement `Debug`, yet we can still deduce it. This might be a concern if we're worried about leaking internal values. If it is, we might want to consider a way to exclude fields (possibly with a `#[reflect(hide)]` macro) or make it purely opt in (as opposed to the default implementation automatically handled by ReflectRef subtraits). Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
![]() |
15acd6f45d |
bevy_reflect: Small refactor and default Reflect methods (#4739)
# Objective Quick followup to #4712. While updating some [other PRs](https://github.com/bevyengine/bevy/pull/4218), I realized the `ReflectTraits` struct could be improved. The issue with the current implementation is that `ReflectTraits::get_xxx_impl(...)` returns just the _logic_ to the corresponding `Reflect` trait method, rather than the entire function. This makes it slightly more annoying to manage since the variable names need to be consistent across files. For example, `get_partial_eq_impl` uses a `value` variable. But the name "value" isn't defined in the `get_partial_eq_impl` method, it's defined in three other methods in a completely separate file. It's not likely to cause any bugs if we keep it as it is since differing variable names will probably just result in a compile error (except in very particular cases). But it would be useful to someone who wanted to edit/add/remove a method. ## Solution Made `get_hash_impl`, `get_partial_eq_impl` and `get_serialize_impl` return the entire method implementation for `reflect_hash`, `reflect_partial_eq`, and `serializable`, respectively. As a result of this, those three `Reflect` methods were also given default implementations. This was fairly simple to do since all three could just be made to return `None`. --- ## Changelog * Small cleanup/refactor to `ReflectTraits` in `bevy_reflect_derive` * Gave `Reflect::reflect_hash`, `Reflect::reflect_partial_eq`, and `Reflect::serializable` default implementations |
||
![]() |
4c194084b4 |
bevy_reflect: Add GetTypeRegistration impl for reflected tuples (#4226)
# Objective Reflected tuples do not implement `GetTypeRegistration`, preventing us from registering our tuples, like: ```rust app.register_type::<(i32, i32)>(); ``` This is especially important for things like using #4042 to improve the scene format or implementing #4154 to recursively register fields. ## Solution Added an implementation to the tuple macro: ```rust impl<$($name: Reflect + for<'de> Deserialize<'de>),*> GetTypeRegistration for ($($name,)*) { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::<($($name,)*)>(); registration.insert::<ReflectDeserialize>(FromType::<($($name,)*)>::from_type()); registration } } ``` This requires that the tuple's types implement `Deserialize`. This is exactly how `Vec` and `HashMap` handle it: ```rust impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T> { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::<Vec<T>>(); registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type()); registration } } ``` |
||
![]() |
5047e1f08e |
bevy_reflect: Add as_reflect and as_reflect_mut (#4350)
# Objective Trait objects that have `Reflect` as a supertrait cannot be upcast to a `dyn Reflect`. Attempting something like: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value as &dyn Reflect; // Error! // ... } ``` Results in `error[E0658]: trait upcasting coercion is experimental`. The reason this is important is that a lot of `bevy_reflect` methods require a `&dyn Reflect`. This is trivial with concrete types, but if we don't know the concrete type (we only have the trait object), we can't use these methods. For example, we couldn't create a `ReflectSerializer` for the type since it expects a `&dyn Reflect` value— even though we should be able to. ## Solution Add `as_reflect` and `as_reflect_mut` to `Reflect` to allow upcasting to a `dyn Reflect`: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value.as_reflect(); // ... } ``` ## Alternatives We could defer this type of logic to the crate/user. They can add these methods to their trait in the same exact way we do here. The main benefit of doing it ourselves is it makes things convenient for them (especially when using the derive macro). We could also create an `AsReflect` trait with a blanket impl over all reflected types, however, I could not get that to work for trait objects since they aren't sized. --- ## Changelog - Added trait method `Reflect::as_reflect(&self)` - Added trait method `Reflect::as_reflect_mut(&mut self)` ## Migration Guide - Manual implementors of `Reflect` will need to add implementations for the methods above (this should be pretty easy as most cases just need to return `self`) |
||
![]() |
d8974e7c3d |
small and mostly pointless refactoring (#2934)
What is says on the tin. This has got more to do with making `clippy` slightly more *quiet* than it does with changing anything that might greatly impact readability or performance. that said, deriving `Default` for a couple of structs is a nice easy win |
||
![]() |
f073b2d7f3 |
document more of bevy_reflect (#3655)
This adds documentation for: - The trait methods of `Reflect` and its subtraits - The `partial_eq` and `apply` functions for `Map` et al. - `DynamicList` and `DynamicMap` - `TypeRegistry` and related types & traits - `GetPath`, including an explanation of path string syntax among other things. Still to be documented are the various macros and `bevy_reflect::serde`. |
||
![]() |
4b4dbb021f |
document Struct , TupleStruct and Tuple (#3081)
# Objective These traits are undocumented on `main`. ## Solution Now they have docs! Included are examples for each trait and their corresponding `GetTypeField` trait. The docs also mention that `#[derive(Reflect)]` will automatically derive the correct subtrait on structs and tuple structs. |