
# 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
275 lines
9.1 KiB
Rust
275 lines
9.1 KiB
Rust
//! Contains code related to field attributes for reflected types.
|
|
//!
|
|
//! A field attribute is an attribute which applies to particular field or variant
|
|
//! as opposed to an entire struct or enum. An example of such an attribute is
|
|
//! the derive helper attribute for `Reflect`, which looks like: `#[reflect(ignore)]`.
|
|
|
|
use crate::{
|
|
attribute_parser::terminated_parser, custom_attributes::CustomAttributes,
|
|
REFLECT_ATTRIBUTE_NAME,
|
|
};
|
|
use quote::ToTokens;
|
|
use syn::{parse::ParseStream, Attribute, LitStr, Meta, Token, Type};
|
|
|
|
mod kw {
|
|
syn::custom_keyword!(ignore);
|
|
syn::custom_keyword!(skip_serializing);
|
|
syn::custom_keyword!(clone);
|
|
syn::custom_keyword!(default);
|
|
syn::custom_keyword!(remote);
|
|
}
|
|
|
|
pub(crate) const IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
|
|
pub(crate) const IGNORE_ALL_ATTR: &str = "ignore";
|
|
|
|
pub(crate) const DEFAULT_ATTR: &str = "default";
|
|
pub(crate) const CLONE_ATTR: &str = "clone";
|
|
|
|
/// Stores data about if the field should be visible via the Reflect and serialization interfaces
|
|
///
|
|
/// Note the relationship between serialization and reflection is such that a member must be reflected in order to be serialized.
|
|
/// In boolean logic this is described as: `is_serialized -> is_reflected`, this means we can reflect something without serializing it but not the other way round.
|
|
/// The `is_reflected` predicate is provided as `self.is_active()`
|
|
#[derive(Default, Clone, Copy, PartialEq, Eq)]
|
|
pub(crate) enum ReflectIgnoreBehavior {
|
|
/// Don't ignore, appear to all systems
|
|
#[default]
|
|
None,
|
|
/// Ignore when serializing but not when reflecting
|
|
IgnoreSerialization,
|
|
/// Ignore both when serializing and reflecting
|
|
IgnoreAlways,
|
|
}
|
|
|
|
impl ReflectIgnoreBehavior {
|
|
/// Returns `true` if the ignoring behavior implies member is included in the reflection API, and false otherwise.
|
|
pub fn is_active(self) -> bool {
|
|
match self {
|
|
ReflectIgnoreBehavior::None | ReflectIgnoreBehavior::IgnoreSerialization => true,
|
|
ReflectIgnoreBehavior::IgnoreAlways => false,
|
|
}
|
|
}
|
|
|
|
/// The exact logical opposite of `self.is_active()` returns true iff this member is not part of the reflection API whatsoever (neither serialized nor reflected)
|
|
pub fn is_ignored(self) -> bool {
|
|
!self.is_active()
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Clone)]
|
|
pub(crate) enum CloneBehavior {
|
|
#[default]
|
|
Default,
|
|
Trait,
|
|
Func(syn::ExprPath),
|
|
}
|
|
|
|
/// Controls how the default value is determined for a field.
|
|
#[derive(Default, Clone)]
|
|
pub(crate) enum DefaultBehavior {
|
|
/// Field is required.
|
|
#[default]
|
|
Required,
|
|
/// Field can be defaulted using `Default::default()`.
|
|
Default,
|
|
/// Field can be created using the given function name.
|
|
///
|
|
/// This assumes the function is in scope, is callable with zero arguments,
|
|
/// and returns the expected type.
|
|
Func(syn::ExprPath),
|
|
}
|
|
|
|
/// A container for attributes defined on a reflected type's field.
|
|
#[derive(Default, Clone)]
|
|
pub(crate) struct FieldAttributes {
|
|
/// Determines how this field should be ignored if at all.
|
|
pub ignore: ReflectIgnoreBehavior,
|
|
/// Sets the clone behavior of this field.
|
|
pub clone: CloneBehavior,
|
|
/// Sets the default behavior of this field.
|
|
pub default: DefaultBehavior,
|
|
/// Custom attributes created via `#[reflect(@...)]`.
|
|
pub custom_attributes: CustomAttributes,
|
|
/// For defining the remote wrapper type that should be used in place of the field for reflection logic.
|
|
pub remote: Option<Type>,
|
|
}
|
|
|
|
impl FieldAttributes {
|
|
/// Parse all field attributes marked "reflect" (such as `#[reflect(ignore)]`).
|
|
pub fn parse_attributes(attrs: &[Attribute]) -> syn::Result<Self> {
|
|
let mut args = FieldAttributes::default();
|
|
|
|
attrs
|
|
.iter()
|
|
.filter_map(|attr| {
|
|
if !attr.path().is_ident(REFLECT_ATTRIBUTE_NAME) {
|
|
// Not a reflect attribute -> skip
|
|
return None;
|
|
}
|
|
|
|
let Meta::List(meta) = &attr.meta else {
|
|
return Some(syn::Error::new_spanned(attr, "expected meta list"));
|
|
};
|
|
|
|
// Parse all attributes inside the list, collecting any errors
|
|
meta.parse_args_with(terminated_parser(Token![,], |stream| {
|
|
args.parse_field_attribute(stream)
|
|
}))
|
|
.err()
|
|
})
|
|
.reduce(|mut acc, err| {
|
|
acc.combine(err);
|
|
acc
|
|
})
|
|
.map_or(Ok(args), Err)
|
|
}
|
|
|
|
/// Parses a single field attribute.
|
|
fn parse_field_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
|
|
let lookahead = input.lookahead1();
|
|
if lookahead.peek(Token![@]) {
|
|
self.parse_custom_attribute(input)
|
|
} else if lookahead.peek(kw::ignore) {
|
|
self.parse_ignore(input)
|
|
} else if lookahead.peek(kw::skip_serializing) {
|
|
self.parse_skip_serializing(input)
|
|
} else if lookahead.peek(kw::clone) {
|
|
self.parse_clone(input)
|
|
} else if lookahead.peek(kw::default) {
|
|
self.parse_default(input)
|
|
} else if lookahead.peek(kw::remote) {
|
|
self.parse_remote(input)
|
|
} else {
|
|
Err(lookahead.error())
|
|
}
|
|
}
|
|
|
|
/// Parse `ignore` attribute.
|
|
///
|
|
/// Examples:
|
|
/// - `#[reflect(ignore)]`
|
|
fn parse_ignore(&mut self, input: ParseStream) -> syn::Result<()> {
|
|
if self.ignore != ReflectIgnoreBehavior::None {
|
|
return Err(input.error(format!(
|
|
"only one of {:?} is allowed",
|
|
[IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
|
|
)));
|
|
}
|
|
|
|
input.parse::<kw::ignore>()?;
|
|
self.ignore = ReflectIgnoreBehavior::IgnoreAlways;
|
|
Ok(())
|
|
}
|
|
|
|
/// Parse `skip_serializing` attribute.
|
|
///
|
|
/// Examples:
|
|
/// - `#[reflect(skip_serializing)]`
|
|
fn parse_skip_serializing(&mut self, input: ParseStream) -> syn::Result<()> {
|
|
if self.ignore != ReflectIgnoreBehavior::None {
|
|
return Err(input.error(format!(
|
|
"only one of {:?} is allowed",
|
|
[IGNORE_ALL_ATTR, IGNORE_SERIALIZATION_ATTR]
|
|
)));
|
|
}
|
|
|
|
input.parse::<kw::skip_serializing>()?;
|
|
self.ignore = ReflectIgnoreBehavior::IgnoreSerialization;
|
|
Ok(())
|
|
}
|
|
|
|
/// Parse `clone` attribute.
|
|
///
|
|
/// Examples:
|
|
/// - `#[reflect(clone)]`
|
|
/// - `#[reflect(clone = "path::to::func")]`
|
|
fn parse_clone(&mut self, input: ParseStream) -> syn::Result<()> {
|
|
if !matches!(self.clone, CloneBehavior::Default) {
|
|
return Err(input.error(format!("only one of {:?} is allowed", [CLONE_ATTR])));
|
|
}
|
|
|
|
input.parse::<kw::clone>()?;
|
|
|
|
if input.peek(Token![=]) {
|
|
input.parse::<Token![=]>()?;
|
|
|
|
let lit = input.parse::<LitStr>()?;
|
|
self.clone = CloneBehavior::Func(lit.parse()?);
|
|
} else {
|
|
self.clone = CloneBehavior::Trait;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Parse `default` attribute.
|
|
///
|
|
/// Examples:
|
|
/// - `#[reflect(default)]`
|
|
/// - `#[reflect(default = "path::to::func")]`
|
|
fn parse_default(&mut self, input: ParseStream) -> syn::Result<()> {
|
|
if !matches!(self.default, DefaultBehavior::Required) {
|
|
return Err(input.error(format!("only one of {:?} is allowed", [DEFAULT_ATTR])));
|
|
}
|
|
|
|
input.parse::<kw::default>()?;
|
|
|
|
if input.peek(Token![=]) {
|
|
input.parse::<Token![=]>()?;
|
|
|
|
let lit = input.parse::<LitStr>()?;
|
|
self.default = DefaultBehavior::Func(lit.parse()?);
|
|
} else {
|
|
self.default = DefaultBehavior::Default;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Parse `@` (custom attribute) attribute.
|
|
///
|
|
/// Examples:
|
|
/// - `#[reflect(@(foo = "bar"))]`
|
|
/// - `#[reflect(@(min = 0.0, max = 1.0))]`
|
|
fn parse_custom_attribute(&mut self, input: ParseStream) -> syn::Result<()> {
|
|
self.custom_attributes.parse_custom_attribute(input)
|
|
}
|
|
|
|
/// Parse `remote` attribute.
|
|
///
|
|
/// Examples:
|
|
/// - `#[reflect(remote = path::to::RemoteType)]`
|
|
fn parse_remote(&mut self, input: ParseStream) -> syn::Result<()> {
|
|
if let Some(remote) = self.remote.as_ref() {
|
|
return Err(input.error(format!(
|
|
"remote type already specified as {}",
|
|
remote.to_token_stream()
|
|
)));
|
|
}
|
|
|
|
input.parse::<kw::remote>()?;
|
|
input.parse::<Token![=]>()?;
|
|
|
|
self.remote = Some(input.parse()?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Returns `Some(true)` if the field has a generic remote type.
|
|
///
|
|
/// If the remote type is not generic, returns `Some(false)`.
|
|
///
|
|
/// If the field does not have a remote type, returns `None`.
|
|
pub fn is_remote_generic(&self) -> Option<bool> {
|
|
if let Type::Path(type_path) = self.remote.as_ref()? {
|
|
type_path
|
|
.path
|
|
.segments
|
|
.last()
|
|
.map(|segment| !segment.arguments.is_empty())
|
|
} else {
|
|
Some(false)
|
|
}
|
|
}
|
|
}
|