
# Objective Bevy code tends to make heavy use of the [newtype]( https://doc.rust-lang.org/rust-by-example/generics/new_types.html) pattern, which is why we have a dedicated derive for [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) and [`DerefMut`](https://doc.rust-lang.org/std/ops/trait.DerefMut.html). This derive works for any struct with a single field: ```rust #[derive(Component, Deref, DerefMut)] struct MyNewtype(usize); ``` One reason for the single-field limitation is to prevent confusion and footguns related that would arise from allowing multi-field structs: <table align="center"> <tr> <th colspan="2"> Similar structs, different derefs </th> </tr> <tr> <td> ```rust #[derive(Deref, DerefMut)] struct MyStruct { foo: usize, // <- Derefs usize bar: String, } ``` </td> <td> ```rust #[derive(Deref, DerefMut)] struct MyStruct { bar: String, // <- Derefs String foo: usize, } ``` </td> </tr> <tr> <th colspan="2"> Why `.1`? </th> </tr> <tr> <td colspan="2"> ```rust #[derive(Deref, DerefMut)] struct MyStruct(Vec<usize>, Vec<f32>); let mut foo = MyStruct(vec![123], vec![1.23]); // Why can we skip the `.0` here? foo.push(456); // But not here? foo.1.push(4.56); ``` </td> </tr> </table> However, there are certainly cases where it's useful to allow for structs with multiple fields. Such as for structs with one "real" field and one `PhantomData` to allow for generics: ```rust #[derive(Deref, DerefMut)] struct MyStruct<T>( // We want use this field for the `Deref`/`DerefMut` impls String, // But we need this field so that we can make this struct generic PhantomData<T> ); // ERROR: Deref can only be derived for structs with a single field // ERROR: DerefMut can only be derived for structs with a single field ``` Additionally, the possible confusion and footguns are mainly an issue for newer Rust/Bevy users. Those familiar with `Deref` and `DerefMut` understand what adding the derive really means and can anticipate its behavior. ## Solution Allow users to opt into multi-field `Deref`/`DerefMut` derives using a `#[deref]` attribute: ```rust #[derive(Deref, DerefMut)] struct MyStruct<T>( // Use this field for the `Deref`/`DerefMut` impls #[deref] String, // We can freely include any other field without a compile error PhantomData<T> ); ``` This prevents the footgun pointed out in the first issue described in the previous section, but it still leaves the possible confusion surrounding `.0`-vs-`.#`. However, the idea is that by making this behavior explicit with an attribute, users will be more aware of it and can adapt appropriately. --- ## Changelog - Added `#[deref]` attribute to `Deref` and `DerefMut` derives
213 lines
5.5 KiB
Rust
213 lines
5.5 KiB
Rust
#![allow(clippy::type_complexity)]
|
|
|
|
extern crate proc_macro;
|
|
|
|
mod app_plugin;
|
|
mod bevy_main;
|
|
mod derefs;
|
|
mod enum_variant_meta;
|
|
|
|
use bevy_macro_utils::{derive_label, BevyManifest};
|
|
use proc_macro::TokenStream;
|
|
use quote::format_ident;
|
|
|
|
/// Generates a dynamic plugin entry point function for the given `Plugin` type.
|
|
#[proc_macro_derive(DynamicPlugin)]
|
|
pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
|
|
app_plugin::derive_dynamic_plugin(input)
|
|
}
|
|
|
|
/// Implements [`Deref`] for structs. This is especially useful when utilizing the [newtype] pattern.
|
|
///
|
|
/// For single-field structs, the implementation automatically uses that field.
|
|
/// For multi-field structs, you must specify which field to use with the `#[deref]` attribute.
|
|
///
|
|
/// If you need [`DerefMut`] as well, consider using the other [derive] macro alongside
|
|
/// this one.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ## Tuple Structs
|
|
///
|
|
/// Using a single-field struct:
|
|
///
|
|
/// ```
|
|
/// use bevy_derive::Deref;
|
|
///
|
|
/// #[derive(Deref)]
|
|
/// struct MyNewtype(String);
|
|
///
|
|
/// let foo = MyNewtype(String::from("Hello"));
|
|
/// assert_eq!("Hello", *foo);
|
|
/// ```
|
|
///
|
|
/// Using a multi-field struct:
|
|
///
|
|
/// ```
|
|
/// # use std::marker::PhantomData;
|
|
/// use bevy_derive::Deref;
|
|
///
|
|
/// #[derive(Deref)]
|
|
/// struct MyStruct<T>(#[deref] String, PhantomData<T>);
|
|
///
|
|
/// let foo = MyStruct(String::from("Hello"), PhantomData::<usize>);
|
|
/// assert_eq!("Hello", *foo);
|
|
/// ```
|
|
///
|
|
/// ## Named Structs
|
|
///
|
|
/// Using a single-field struct:
|
|
///
|
|
/// ```
|
|
/// use bevy_derive::{Deref, DerefMut};
|
|
///
|
|
/// #[derive(Deref, DerefMut)]
|
|
/// struct MyStruct {
|
|
/// value: String,
|
|
/// }
|
|
///
|
|
/// let foo = MyStruct {
|
|
/// value: String::from("Hello")
|
|
/// };
|
|
/// assert_eq!("Hello", *foo);
|
|
/// ```
|
|
///
|
|
/// Using a multi-field struct:
|
|
///
|
|
/// ```
|
|
/// # use std::marker::PhantomData;
|
|
/// use bevy_derive::{Deref, DerefMut};
|
|
///
|
|
/// #[derive(Deref, DerefMut)]
|
|
/// struct MyStruct<T> {
|
|
/// #[deref]
|
|
/// value: String,
|
|
/// _phantom: PhantomData<T>,
|
|
/// }
|
|
///
|
|
/// let foo = MyStruct {
|
|
/// value:String::from("Hello"),
|
|
/// _phantom:PhantomData::<usize>
|
|
/// };
|
|
/// assert_eq!("Hello", *foo);
|
|
/// ```
|
|
///
|
|
/// [`Deref`]: std::ops::Deref
|
|
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
|
|
/// [`DerefMut`]: std::ops::DerefMut
|
|
/// [derive]: crate::derive_deref_mut
|
|
#[proc_macro_derive(Deref, attributes(deref))]
|
|
pub fn derive_deref(input: TokenStream) -> TokenStream {
|
|
derefs::derive_deref(input)
|
|
}
|
|
|
|
/// Implements [`DerefMut`] for structs. This is especially useful when utilizing the [newtype] pattern.
|
|
///
|
|
/// For single-field structs, the implementation automatically uses that field.
|
|
/// For multi-field structs, you must specify which field to use with the `#[deref]` attribute.
|
|
///
|
|
/// [`DerefMut`] requires a [`Deref`] implementation. You can implement it manually or use
|
|
/// Bevy's [derive] macro for convenience.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ## Tuple Structs
|
|
///
|
|
/// Using a single-field struct:
|
|
///
|
|
/// ```
|
|
/// use bevy_derive::{Deref, DerefMut};
|
|
///
|
|
/// #[derive(Deref, DerefMut)]
|
|
/// struct MyNewtype(String);
|
|
///
|
|
/// let mut foo = MyNewtype(String::from("Hello"));
|
|
/// foo.push_str(" World!");
|
|
/// assert_eq!("Hello World!", *foo);
|
|
/// ```
|
|
///
|
|
/// Using a multi-field struct:
|
|
///
|
|
/// ```
|
|
/// # use std::marker::PhantomData;
|
|
/// use bevy_derive::{Deref, DerefMut};
|
|
///
|
|
/// #[derive(Deref, DerefMut)]
|
|
/// struct MyStruct<T>(#[deref] String, PhantomData<T>);
|
|
///
|
|
/// let mut foo = MyStruct(String::from("Hello"), PhantomData::<usize>);
|
|
/// foo.push_str(" World!");
|
|
/// assert_eq!("Hello World!", *foo);
|
|
/// ```
|
|
///
|
|
/// ## Named Structs
|
|
///
|
|
/// Using a single-field struct:
|
|
///
|
|
/// ```
|
|
/// use bevy_derive::{Deref, DerefMut};
|
|
///
|
|
/// #[derive(Deref, DerefMut)]
|
|
/// struct MyStruct {
|
|
/// value: String,
|
|
/// }
|
|
///
|
|
/// let mut foo = MyStruct {
|
|
/// value: String::from("Hello")
|
|
/// };
|
|
/// foo.push_str(" World!");
|
|
/// assert_eq!("Hello World!", *foo);
|
|
/// ```
|
|
///
|
|
/// Using a multi-field struct:
|
|
///
|
|
/// ```
|
|
/// # use std::marker::PhantomData;
|
|
/// use bevy_derive::{Deref, DerefMut};
|
|
///
|
|
/// #[derive(Deref, DerefMut)]
|
|
/// struct MyStruct<T> {
|
|
/// #[deref]
|
|
/// value: String,
|
|
/// _phantom: PhantomData<T>,
|
|
/// }
|
|
///
|
|
/// let mut foo = MyStruct {
|
|
/// value:String::from("Hello"),
|
|
/// _phantom:PhantomData::<usize>
|
|
/// };
|
|
/// foo.push_str(" World!");
|
|
/// assert_eq!("Hello World!", *foo);
|
|
/// ```
|
|
///
|
|
/// [`DerefMut`]: std::ops::DerefMut
|
|
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
|
|
/// [`Deref`]: std::ops::Deref
|
|
/// [derive]: crate::derive_deref
|
|
#[proc_macro_derive(DerefMut, attributes(deref))]
|
|
pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
|
|
derefs::derive_deref_mut(input)
|
|
}
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn bevy_main(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
bevy_main::bevy_main(attr, item)
|
|
}
|
|
|
|
#[proc_macro_derive(EnumVariantMeta)]
|
|
pub fn derive_enum_variant_meta(input: TokenStream) -> TokenStream {
|
|
enum_variant_meta::derive_enum_variant_meta(input)
|
|
}
|
|
|
|
/// Generates an impl of the `AppLabel` trait.
|
|
///
|
|
/// This works only for unit structs, or enums with only unit variants.
|
|
/// You may force a struct or variant to behave as if it were fieldless with `#[app_label(ignore_fields)]`.
|
|
#[proc_macro_derive(AppLabel, attributes(app_label))]
|
|
pub fn derive_app_label(input: TokenStream) -> TokenStream {
|
|
let input = syn::parse_macro_input!(input as syn::DeriveInput);
|
|
let mut trait_path = BevyManifest::default().get_path("bevy_app");
|
|
trait_path.segments.push(format_ident!("AppLabel").into());
|
|
derive_label(input, &trait_path, "app_label")
|
|
}
|