diff --git a/benches/benches/bevy_reflect/function.rs b/benches/benches/bevy_reflect/function.rs index 79b01a2c0f..4eb97eee82 100644 --- a/benches/benches/bevy_reflect/function.rs +++ b/benches/benches/bevy_reflect/function.rs @@ -1,4 +1,4 @@ -use bevy_reflect::func::{ArgList, IntoClosure, TypedFunction}; +use bevy_reflect::func::{ArgList, IntoFunction, TypedFunction}; use bevy_reflect::prelude::*; use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; @@ -29,7 +29,7 @@ fn into(c: &mut Criterion) { .bench_function("closure", |b| { let capture = 25; let closure = |a: i32| a + capture; - b.iter(|| closure.into_closure()); + b.iter(|| closure.into_function()); }); } @@ -45,7 +45,7 @@ fn call(c: &mut Criterion) { }) .bench_function("closure", |b| { let capture = 25; - let add = (|a: i32| a + capture).into_closure(); + let add = (|a: i32| a + capture).into_function(); b.iter_batched( || ArgList::new().push_owned(75_i32), |args| add.call(args), diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index d1fa331a23..e4fa508a69 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -605,16 +605,16 @@ impl App { /// Registers the given function into the [`AppFunctionRegistry`] resource. /// - /// The given function will internally be stored as a [`DynamicClosure`] + /// The given function will internally be stored as a [`DynamicFunction`] /// and mapped according to its [name]. /// /// Because the function must have a name, /// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead - /// be registered using [`register_function_with_name`] or converted to a [`DynamicClosure`] - /// and named using [`DynamicClosure::with_name`]. + /// be registered using [`register_function_with_name`] or converted to a [`DynamicFunction`] + /// and named using [`DynamicFunction::with_name`]. /// Failure to do so will result in a panic. /// - /// Only types that implement [`IntoClosure`] may be registered via this method. + /// Only types that implement [`IntoFunction`] may be registered via this method. /// /// See [`FunctionRegistry::register`] for more information. /// @@ -650,7 +650,7 @@ impl App { /// .register_function(add); /// ``` /// - /// Anonymous functions and closures should be registered using [`register_function_with_name`] or given a name using [`DynamicClosure::with_name`]. + /// Anonymous functions and closures should be registered using [`register_function_with_name`] or given a name using [`DynamicFunction::with_name`]. /// /// ```should_panic /// use bevy_app::App; @@ -660,15 +660,15 @@ impl App { /// ``` /// /// [`register_function_with_name`]: Self::register_function_with_name - /// [`DynamicClosure`]: bevy_reflect::func::DynamicClosure + /// [`DynamicFunction`]: bevy_reflect::func::DynamicFunction /// [name]: bevy_reflect::func::FunctionInfo::name - /// [`DynamicClosure::with_name`]: bevy_reflect::func::DynamicClosure::with_name - /// [`IntoClosure`]: bevy_reflect::func::IntoClosure + /// [`DynamicFunction::with_name`]: bevy_reflect::func::DynamicFunction::with_name + /// [`IntoFunction`]: bevy_reflect::func::IntoFunction /// [`FunctionRegistry::register`]: bevy_reflect::func::FunctionRegistry::register #[cfg(feature = "reflect_functions")] pub fn register_function(&mut self, function: F) -> &mut Self where - F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static, + F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static, { self.main_mut().register_function(function); self @@ -689,7 +689,7 @@ impl App { /// For named functions (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`) where a custom name is not needed, /// it's recommended to use [`register_function`] instead as the generated name is guaranteed to be unique. /// - /// Only types that implement [`IntoClosure`] may be registered via this method. + /// Only types that implement [`IntoFunction`] may be registered via this method. /// /// See [`FunctionRegistry::register_with_name`] for more information. /// @@ -738,7 +738,7 @@ impl App { /// /// [type name]: std::any::type_name /// [`register_function`]: Self::register_function - /// [`IntoClosure`]: bevy_reflect::func::IntoClosure + /// [`IntoFunction`]: bevy_reflect::func::IntoFunction /// [`FunctionRegistry::register_with_name`]: bevy_reflect::func::FunctionRegistry::register_with_name #[cfg(feature = "reflect_functions")] pub fn register_function_with_name( @@ -747,7 +747,7 @@ impl App { function: F, ) -> &mut Self where - F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static, + F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static, { self.main_mut().register_function_with_name(name, function); self diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs index fc91ca2f34..48ffe97388 100644 --- a/crates/bevy_app/src/sub_app.rs +++ b/crates/bevy_app/src/sub_app.rs @@ -413,7 +413,7 @@ impl SubApp { #[cfg(feature = "reflect_functions")] pub fn register_function(&mut self, function: F) -> &mut Self where - F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static, + F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static, { let registry = self.world.resource_mut::(); registry.write().register(function).unwrap(); @@ -428,7 +428,7 @@ impl SubApp { function: F, ) -> &mut Self where - F: bevy_reflect::func::IntoClosure<'static, Marker> + 'static, + F: bevy_reflect::func::IntoFunction<'static, Marker> + 'static, { let registry = self.world.resource_mut::(); registry.write().register_with_name(name, function).unwrap(); diff --git a/crates/bevy_reflect/compile_fail/tests/into_function/closure_fail.rs b/crates/bevy_reflect/compile_fail/tests/into_function/closure_fail.rs index 15316677ca..0083198534 100644 --- a/crates/bevy_reflect/compile_fail/tests/into_function/closure_fail.rs +++ b/crates/bevy_reflect/compile_fail/tests/into_function/closure_fail.rs @@ -1,19 +1,18 @@ #![allow(unused)] -use bevy_reflect::func::IntoFunction; +use bevy_reflect::func::{DynamicFunction, IntoFunction}; use bevy_reflect::Reflect; fn main() { let value = String::from("Hello, World!"); let closure_capture_owned = move || println!("{}", value); - let _ = closure_capture_owned.into_function(); - //~^ E0277 + // Pass: + let _: DynamicFunction<'static> = closure_capture_owned.into_function(); let value = String::from("Hello, World!"); let closure_capture_reference = || println!("{}", value); + //~^ ERROR: `value` does not live long enough - let _ = closure_capture_reference.into_function(); - // ↑ This should be an error (E0277) but `compile_fail_utils` fails to pick it up - // when the `closure_capture_owned` test is present + let _: DynamicFunction<'static> = closure_capture_reference.into_function(); } diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs index aa0d12b027..14c02eecf1 100644 --- a/crates/bevy_reflect/src/func/args/arg.rs +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -2,12 +2,10 @@ use crate::func::args::{ArgError, FromArg, Ownership}; use crate::{PartialReflect, Reflect, TypePath}; use std::ops::Deref; -/// Represents an argument that can be passed to a [`DynamicFunction`], [`DynamicClosure`], -/// or [`DynamicClosureMut`]. +/// Represents an argument that can be passed to a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction -/// [`DynamicClosure`]: crate::func::DynamicClosure -/// [`DynamicClosureMut`]: crate::func::DynamicClosureMut +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug)] pub struct Arg<'a> { index: usize, @@ -181,9 +179,10 @@ impl<'a> Arg<'a> { } } -/// Represents an argument that can be passed to a [`DynamicFunction`]. +/// Represents an argument that can be passed to a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug)] pub enum ArgValue<'a> { Owned(Box), diff --git a/crates/bevy_reflect/src/func/args/from_arg.rs b/crates/bevy_reflect/src/func/args/from_arg.rs index 9478650165..88d04aefe7 100644 --- a/crates/bevy_reflect/src/func/args/from_arg.rs +++ b/crates/bevy_reflect/src/func/args/from_arg.rs @@ -3,16 +3,16 @@ use crate::func::args::{Arg, ArgError}; /// A trait for types that can be created from an [`Arg`]. /// /// This trait exists so that types can be automatically converted into an [`Arg`] -/// by [`IntoFunction`] so they can be passed to a [`DynamicFunction`] in an [`ArgList`]. +/// so they can be put into an [`ArgList`] and passed to a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// This trait is used instead of a blanket [`From`] implementation due to coherence issues: /// we can't implement `From` for both `T` and `&T`/`&mut T`. /// /// This trait is automatically implemented when using the `Reflect` [derive macro]. /// -/// [`IntoFunction`]: crate::func::IntoFunction -/// [`DynamicFunction`]: crate::func::DynamicFunction /// [`ArgList`]: crate::func::args::ArgList +/// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut /// [derive macro]: derive@crate::Reflect pub trait FromArg { /// The type to convert into. diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index f55643dcc7..e932e77be3 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -3,13 +3,11 @@ use alloc::borrow::Cow; use crate::func::args::{GetOwnership, Ownership}; use crate::TypePath; -/// Type information for an [`Arg`] used in a [`DynamicFunction`], [`DynamicClosure`], -/// or [`DynamicClosureMut`]. +/// Type information for an [`Arg`] used in a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// [`Arg`]: crate::func::args::Arg -/// [`DynamicFunction`]: crate::func::function::DynamicFunction -/// [`DynamicClosure`]: crate::func::closures::DynamicClosure -/// [`DynamicClosureMut`]: crate::func::closures::DynamicClosureMut +/// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug, Clone)] pub struct ArgInfo { /// The index of the argument within its function. @@ -58,13 +56,13 @@ impl ArgInfo { /// since the name can't be inferred from the function type alone. /// /// For [`DynamicFunctions`] created using [`IntoFunction`] - /// or [`DynamicClosures`] created using [`IntoClosure`], + /// and [`DynamicFunctionMuts`] created using [`IntoFunctionMut`], /// the name will always be `None`. /// /// [`DynamicFunctions`]: crate::func::DynamicFunction /// [`IntoFunction`]: crate::func::IntoFunction - /// [`DynamicClosures`]: crate::func::DynamicClosure - /// [`IntoClosure`]: crate::func::IntoClosure + /// [`DynamicFunctionMuts`]: crate::func::DynamicFunctionMut + /// [`IntoFunctionMut`]: crate::func::IntoFunctionMut pub fn name(&self) -> Option<&str> { self.name.as_deref() } @@ -74,6 +72,9 @@ impl ArgInfo { self.ownership } + /// The [type path] of the argument. + /// + /// [type path]: TypePath::type_path pub fn type_path(&self) -> &'static str { self.type_path } diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index 481df444f2..15458250f4 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -3,8 +3,7 @@ use crate::func::ArgError; use crate::{PartialReflect, Reflect, TypePath}; use std::collections::VecDeque; -/// A list of arguments that can be passed to a [`DynamicFunction`], [`DynamicClosure`], -/// or [`DynamicClosureMut`]. +/// A list of arguments that can be passed to a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// # Example /// @@ -28,8 +27,7 @@ use std::collections::VecDeque; /// /// [arguments]: Arg /// [`DynamicFunction`]: crate::func::DynamicFunction -/// [`DynamicClosure`]: crate::func::DynamicClosure -/// [`DynamicClosureMut`]: crate::func::DynamicClosureMut +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Default, Debug)] pub struct ArgList<'a> { list: VecDeque>, diff --git a/crates/bevy_reflect/src/func/args/mod.rs b/crates/bevy_reflect/src/func/args/mod.rs index 57237b70e8..da0ea00bb1 100644 --- a/crates/bevy_reflect/src/func/args/mod.rs +++ b/crates/bevy_reflect/src/func/args/mod.rs @@ -1,7 +1,7 @@ -//! Argument types and utilities for working with [`DynamicFunctions`] and [`DynamicClosures`]. +//! Argument types and utilities for working with [`DynamicFunction`] and [`DynamicFunctionMut`]. //! -//! [`DynamicFunctions`]: crate::func::DynamicFunction -//! [`DynamicClosures`]: crate::func::DynamicClosure +//! [`DynamicFunction`]: crate::func::DynamicFunction +//! [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut pub use arg::*; pub use error::*; diff --git a/crates/bevy_reflect/src/func/args/ownership.rs b/crates/bevy_reflect/src/func/args/ownership.rs index e12ca5440a..b9395c742f 100644 --- a/crates/bevy_reflect/src/func/args/ownership.rs +++ b/crates/bevy_reflect/src/func/args/ownership.rs @@ -2,12 +2,12 @@ use core::fmt::{Display, Formatter}; /// A trait for getting the ownership of a type. /// -/// This trait exists so that [`IntoFunction`] can automatically generate +/// This trait exists so that [`TypedFunction`] can automatically generate /// [`FunctionInfo`] containing the proper [`Ownership`] for its [argument] types. /// /// This trait is automatically implemented when using the `Reflect` [derive macro]. /// -/// [`IntoFunction`]: crate::func::IntoFunction +/// [`TypedFunction`]: crate::func::TypedFunction /// [`FunctionInfo`]: crate::func::FunctionInfo /// [argument]: crate::func::args::Arg /// [derive macro]: derive@crate::Reflect diff --git a/crates/bevy_reflect/src/func/closures/dynamic_closure.rs b/crates/bevy_reflect/src/func/closures/dynamic_closure.rs deleted file mode 100644 index f49645f70f..0000000000 --- a/crates/bevy_reflect/src/func/closures/dynamic_closure.rs +++ /dev/null @@ -1,240 +0,0 @@ -use crate::func::args::{ArgInfo, ArgList}; -use crate::func::info::FunctionInfo; -use crate::func::{ - DynamicClosureMut, DynamicFunction, FunctionResult, IntoClosure, IntoClosureMut, ReturnInfo, -}; -use alloc::borrow::Cow; -use core::fmt::{Debug, Formatter}; -use std::sync::Arc; - -/// A dynamic representation of a Rust closure. -/// -/// This type can be used to represent any Rust closure that captures its environment immutably. -/// For closures that need to capture their environment mutably, -/// see [`DynamicClosureMut`]. -/// -/// This type can be seen as a superset of [`DynamicFunction`]. -/// -/// See the [module-level documentation] for more information. -/// -/// You will generally not need to construct this manually. -/// Instead, many functions and closures can be automatically converted using the [`IntoClosure`] trait. -/// -/// # Example -/// -/// Most of the time, a [`DynamicClosure`] can be created using the [`IntoClosure`] trait: -/// -/// ``` -/// # use bevy_reflect::func::{ArgList, DynamicClosure, FunctionInfo, IntoClosure}; -/// # -/// let punct = String::from("!!!"); -/// -/// let punctuate = |text: &String| -> String { -/// format!("{}{}", text, punct) -/// }; -/// -/// // Convert the closure into a dynamic closure using `IntoClosure::into_closure` -/// let mut func: DynamicClosure = punctuate.into_closure(); -/// -/// // Dynamically call the closure: -/// let text = String::from("Hello, world"); -/// let args = ArgList::default().push_ref(&text); -/// let value = func.call(args).unwrap().unwrap_owned(); -/// -/// // Check the result: -/// assert_eq!(value.try_take::().unwrap(), "Hello, world!!!"); -/// ``` -pub struct DynamicClosure<'env> { - pub(super) info: FunctionInfo, - pub(super) func: Arc Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>, -} - -impl<'env> DynamicClosure<'env> { - /// Create a new [`DynamicClosure`]. - /// - /// The given function can be used to call out to a regular function, closure, or method. - /// - /// It's important that the closure signature matches the provided [`FunctionInfo`]. - /// This info may be used by consumers of the function for validation and debugging. - pub fn new Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>( - func: F, - info: FunctionInfo, - ) -> Self { - Self { - info, - func: Arc::new(func), - } - } - - /// Set the name of the closure. - /// - /// For [`DynamicClosures`] created using [`IntoClosure`], - /// the default name will always be the full path to the closure as returned by [`std::any::type_name`]. - /// - /// This default name generally does not contain the actual name of the closure, only its module path. - /// It is therefore recommended to set the name manually using this method. - /// - /// [`DynamicClosures`]: DynamicClosure - pub fn with_name(mut self, name: impl Into>) -> Self { - self.info = self.info.with_name(name); - self - } - - /// Set the arguments of the closure. - /// - /// It's important that the arguments match the intended closure signature, - /// as this can be used by consumers of the function for validation and debugging. - pub fn with_args(mut self, args: Vec) -> Self { - self.info = self.info.with_args(args); - self - } - - /// Set the return information of the closure. - pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { - self.info = self.info.with_return_info(return_info); - self - } - - /// Call the closure with the given arguments. - /// - /// # Example - /// - /// ``` - /// # use bevy_reflect::func::{IntoClosure, ArgList}; - /// let c = 23; - /// let add = |a: i32, b: i32| -> i32 { - /// a + b + c - /// }; - /// - /// let mut func = add.into_closure().with_name("add"); - /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - /// let result = func.call(args).unwrap().unwrap_owned(); - /// assert_eq!(result.try_take::().unwrap(), 123); - /// ``` - pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> { - (self.func)(args) - } - - /// Returns the closure info. - pub fn info(&self) -> &FunctionInfo { - &self.info - } - - /// The [name] of the closure. - /// - /// If this [`DynamicClosure`] was created using [`IntoClosure`], - /// then the default name will always be `None`. - /// - /// This can be overridden using [`with_name`]. - /// - /// [name]: FunctionInfo::name - /// [`with_name`]: Self::with_name - pub fn name(&self) -> Option<&Cow<'static, str>> { - self.info.name() - } -} - -/// Outputs the closure's signature. -/// -/// This takes the format: `DynamicClosure(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. -/// -/// Names for arguments and the closure itself are optional and will default to `_` if not provided. -impl<'env> Debug for DynamicClosure<'env> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let name = self.info.name().unwrap_or(&Cow::Borrowed("_")); - write!(f, "DynamicClosure(fn {name}(")?; - - for (index, arg) in self.info.args().iter().enumerate() { - let name = arg.name().unwrap_or("_"); - let ty = arg.type_path(); - write!(f, "{name}: {ty}")?; - - if index + 1 < self.info.args().len() { - write!(f, ", ")?; - } - } - - let ret = self.info.return_info().type_path(); - write!(f, ") -> {ret})") - } -} - -impl<'env> Clone for DynamicClosure<'env> { - fn clone(&self) -> Self { - Self { - info: self.info.clone(), - func: Arc::clone(&self.func), - } - } -} - -impl From for DynamicClosure<'static> { - #[inline] - fn from(func: DynamicFunction) -> Self { - Self { - info: func.info, - func: func.func, - } - } -} - -impl<'env> IntoClosure<'env, ()> for DynamicClosure<'env> { - #[inline] - fn into_closure(self) -> DynamicClosure<'env> { - self - } -} - -impl<'env> IntoClosureMut<'env, ()> for DynamicClosure<'env> { - #[inline] - fn into_closure_mut(self) -> DynamicClosureMut<'env> { - DynamicClosureMut::from(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_overwrite_closure_name() { - let c = 23; - let func = (|a: i32, b: i32| a + b + c) - .into_closure() - .with_name("my_closure"); - assert_eq!(func.info().name().unwrap(), "my_closure"); - } - - #[test] - fn should_convert_dynamic_closure_with_into_closure() { - fn make_closure<'env, F: IntoClosure<'env, M>, M>(f: F) -> DynamicClosure<'env> { - f.into_closure() - } - - let c = 23; - let closure: DynamicClosure = make_closure(|a: i32, b: i32| a + b + c); - let _: DynamicClosure = make_closure(closure); - } - - #[test] - fn should_clone_dynamic_closure() { - let hello = String::from("Hello"); - - let greet = |name: &String| -> String { format!("{}, {}!", hello, name) }; - - let greet = greet.into_closure().with_name("greet"); - let clone = greet.clone(); - - assert_eq!(greet.name().unwrap(), "greet"); - assert_eq!(clone.name().unwrap(), "greet"); - - let clone_value = clone - .call(ArgList::default().push_ref(&String::from("world"))) - .unwrap() - .unwrap_owned() - .try_take::() - .unwrap(); - - assert_eq!(clone_value, "Hello, world!"); - } -} diff --git a/crates/bevy_reflect/src/func/closures/dynamic_closure_mut.rs b/crates/bevy_reflect/src/func/closures/dynamic_closure_mut.rs deleted file mode 100644 index b85521d939..0000000000 --- a/crates/bevy_reflect/src/func/closures/dynamic_closure_mut.rs +++ /dev/null @@ -1,252 +0,0 @@ -use alloc::borrow::Cow; -use core::fmt::{Debug, Formatter}; - -use crate::func::args::{ArgInfo, ArgList}; -use crate::func::info::FunctionInfo; -use crate::func::{DynamicClosure, DynamicFunction, FunctionResult, IntoClosureMut, ReturnInfo}; - -/// A dynamic representation of a Rust closure. -/// -/// This type can be used to represent any Rust closure that captures its environment mutably. -/// For closures that only need to capture their environment immutably, -/// consider using [`DynamicClosure`]. -/// -/// This type can be seen as a superset of [`DynamicClosure`]. -/// -/// See the [module-level documentation] for more information. -/// -/// You will generally not need to construct this manually. -/// Instead, many functions and closures can be automatically converted using the [`IntoClosureMut`] trait. -/// -/// # Example -/// -/// Most of the time, a [`DynamicClosureMut`] can be created using the [`IntoClosureMut`] trait: -/// -/// ``` -/// # use bevy_reflect::func::{ArgList, DynamicClosureMut, FunctionInfo, IntoClosureMut}; -/// # -/// let mut list: Vec = vec![1, 2, 3]; -/// -/// // `replace` is a closure that captures a mutable reference to `list` -/// let mut replace = |index: usize, value: i32| -> i32 { -/// let old_value = list[index]; -/// list[index] = value; -/// old_value -/// }; -/// -/// // Convert the closure into a dynamic closure using `IntoClosureMut::into_closure_mut` -/// let mut func: DynamicClosureMut = replace.into_closure_mut(); -/// -/// // Dynamically call the closure: -/// let args = ArgList::default().push_owned(1_usize).push_owned(-2_i32); -/// let value = func.call(args).unwrap().unwrap_owned(); -/// -/// // Check the result: -/// assert_eq!(value.try_take::().unwrap(), 2); -/// -/// // Note that `func` still has a reference to `list`, -/// // so we need to drop it before we can access `list` again. -/// // Alternatively, we could have called the `func` using -/// // `DynamicClosureMut::call_once` to immediately consume the closure. -/// drop(func); -/// assert_eq!(list, vec![1, -2, 3]); -/// ``` -pub struct DynamicClosureMut<'env> { - info: FunctionInfo, - func: Box FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>, -} - -impl<'env> DynamicClosureMut<'env> { - /// Create a new [`DynamicClosureMut`]. - /// - /// The given function can be used to call out to a regular function, closure, or method. - /// - /// It's important that the closure signature matches the provided [`FunctionInfo`]. - /// This info may be used by consumers of the function for validation and debugging. - pub fn new FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>( - func: F, - info: FunctionInfo, - ) -> Self { - Self { - info, - func: Box::new(func), - } - } - - /// Set the name of the closure. - /// - /// For [`DynamicClosureMuts`] created using [`IntoClosureMut`], - /// the default name will always be the full path to the closure as returned by [`std::any::type_name`]. - /// - /// This default name generally does not contain the actual name of the closure, only its module path. - /// It is therefore recommended to set the name manually using this method. - /// - /// [`DynamicClosureMuts`]: DynamicClosureMut - pub fn with_name(mut self, name: impl Into>) -> Self { - self.info = self.info.with_name(name); - self - } - - /// Set the arguments of the closure. - /// - /// It's important that the arguments match the intended closure signature, - /// as this can be used by consumers of the function for validation and debugging. - pub fn with_args(mut self, args: Vec) -> Self { - self.info = self.info.with_args(args); - self - } - - /// Set the return information of the closure. - pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { - self.info = self.info.with_return_info(return_info); - self - } - - /// Call the closure with the given arguments. - /// - /// Variables that are captured mutably by this closure - /// won't be usable until this closure is dropped. - /// Consider using [`call_once`] if you want to consume the closure - /// immediately after calling it. - /// - /// # Example - /// - /// ``` - /// # use bevy_reflect::func::{IntoClosureMut, ArgList}; - /// let mut total = 0; - /// let add = |a: i32, b: i32| -> i32 { - /// total = a + b; - /// total - /// }; - /// - /// let mut func = add.into_closure_mut().with_name("add"); - /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - /// let result = func.call(args).unwrap().unwrap_owned(); - /// assert_eq!(result.try_take::().unwrap(), 100); - /// ``` - /// - /// [`call_once`]: DynamicClosureMut::call_once - pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> { - (self.func)(args) - } - - /// Call the closure with the given arguments and consume the closure. - /// - /// This is useful for closures that capture their environment mutably - /// because otherwise any captured variables would still be borrowed by this closure. - /// - /// # Example - /// - /// ``` - /// # use bevy_reflect::func::{IntoClosureMut, ArgList}; - /// let mut count = 0; - /// let increment = |amount: i32| count += amount; - /// - /// let increment_function = increment.into_closure_mut(); - /// let args = ArgList::new().push_owned(5_i32); - /// - /// // We need to drop `increment_function` here so that we - /// // can regain access to `count`. - /// // `call_once` does this automatically for us. - /// increment_function.call_once(args).unwrap(); - /// assert_eq!(count, 5); - /// ``` - pub fn call_once(mut self, args: ArgList) -> FunctionResult { - (self.func)(args) - } - - /// Returns the closure info. - pub fn info(&self) -> &FunctionInfo { - &self.info - } - - /// The [name] of the closure. - /// - /// If this [`DynamicClosureMut`] was created using [`IntoClosureMut`], - /// then the default name will always be `None`. - /// - /// This can be overridden using [`with_name`]. - /// - /// [name]: FunctionInfo::name - /// [`with_name`]: Self::with_name - pub fn name(&self) -> Option<&Cow<'static, str>> { - self.info.name() - } -} - -/// Outputs the closure's signature. -/// -/// This takes the format: `DynamicClosureMut(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. -/// -/// Names for arguments and the closure itself are optional and will default to `_` if not provided. -impl<'env> Debug for DynamicClosureMut<'env> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let name = self.info.name().unwrap_or(&Cow::Borrowed("_")); - write!(f, "DynamicClosureMut(fn {name}(")?; - - for (index, arg) in self.info.args().iter().enumerate() { - let name = arg.name().unwrap_or("_"); - let ty = arg.type_path(); - write!(f, "{name}: {ty}")?; - - if index + 1 < self.info.args().len() { - write!(f, ", ")?; - } - } - - let ret = self.info.return_info().type_path(); - write!(f, ") -> {ret})") - } -} - -impl From for DynamicClosureMut<'static> { - #[inline] - fn from(func: DynamicFunction) -> Self { - Self { - info: func.info, - func: Box::new(move |args| (func.func)(args)), - } - } -} - -impl<'env> From> for DynamicClosureMut<'env> { - #[inline] - fn from(closure: DynamicClosure<'env>) -> Self { - Self { - info: closure.info, - func: Box::new(move |args| (closure.func)(args)), - } - } -} - -impl<'env> IntoClosureMut<'env, ()> for DynamicClosureMut<'env> { - #[inline] - fn into_closure_mut(self) -> DynamicClosureMut<'env> { - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_overwrite_closure_name() { - let mut total = 0; - let func = (|a: i32, b: i32| total = a + b) - .into_closure_mut() - .with_name("my_closure"); - assert_eq!(func.info().name().unwrap(), "my_closure"); - } - - #[test] - fn should_convert_dynamic_closure_mut_with_into_closure() { - fn make_closure<'env, F: IntoClosureMut<'env, M>, M>(f: F) -> DynamicClosureMut<'env> { - f.into_closure_mut() - } - - let mut total = 0; - let closure: DynamicClosureMut = make_closure(|a: i32, b: i32| total = a + b); - let _: DynamicClosureMut = make_closure(closure); - } -} diff --git a/crates/bevy_reflect/src/func/closures/into_closure.rs b/crates/bevy_reflect/src/func/closures/into_closure.rs deleted file mode 100644 index f3cba4a5d1..0000000000 --- a/crates/bevy_reflect/src/func/closures/into_closure.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::func::{DynamicClosure, ReflectFn, TypedFunction}; - -/// A trait for types that can be converted into a [`DynamicClosure`]. -/// -/// This trait is automatically implemented for any type that implements -/// [`ReflectFn`] and [`TypedFunction`]. -/// -/// This trait can be seen as a supertrait of [`IntoFunction`]. -/// -/// See the [module-level documentation] for more information. -/// -/// [`IntoFunction`]: crate::func::IntoFunction -/// [module-level documentation]: crate::func -pub trait IntoClosure<'env, Marker> { - /// Converts [`Self`] into a [`DynamicClosure`]. - fn into_closure(self) -> DynamicClosure<'env>; -} - -impl<'env, F, Marker1, Marker2> IntoClosure<'env, (Marker1, Marker2)> for F -where - F: ReflectFn<'env, Marker1> + TypedFunction + Send + Sync + 'env, -{ - fn into_closure(self) -> DynamicClosure<'env> { - DynamicClosure::new(move |args| self.reflect_call(args), Self::function_info()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::func::ArgList; - - #[test] - fn should_create_dynamic_closure_from_closure() { - let c = 23; - let func = (|a: i32, b: i32| a + b + c).into_closure(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.try_downcast_ref::(), Some(&123)); - } - - #[test] - fn should_create_dynamic_closure_from_function() { - fn add(a: i32, b: i32) -> i32 { - a + b - } - - let func = add.into_closure(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.try_downcast_ref::(), Some(&100)); - } - - #[test] - fn should_default_closure_name_to_none() { - let c = 23; - let func = (|a: i32, b: i32| a + b + c).into_closure(); - assert_eq!(func.info().name(), None); - } -} diff --git a/crates/bevy_reflect/src/func/closures/into_closure_mut.rs b/crates/bevy_reflect/src/func/closures/into_closure_mut.rs deleted file mode 100644 index 96359fcedc..0000000000 --- a/crates/bevy_reflect/src/func/closures/into_closure_mut.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::func::{DynamicClosureMut, ReflectFnMut, TypedFunction}; - -/// A trait for types that can be converted into a [`DynamicClosureMut`]. -/// -/// This trait is automatically implemented for any type that implements -/// [`ReflectFnMut`] and [`TypedFunction`]. -/// -/// This trait can be seen as a supertrait of [`IntoClosure`]. -/// -/// See the [module-level documentation] for more information. -/// -/// [`ReflectFn`]: crate::func::ReflectFn -/// [`IntoClosure`]: crate::func::closures::IntoClosure -/// [module-level documentation]: crate::func -pub trait IntoClosureMut<'env, Marker> { - /// Converts [`Self`] into a [`DynamicClosureMut`]. - fn into_closure_mut(self) -> DynamicClosureMut<'env>; -} - -impl<'env, F, Marker1, Marker2> IntoClosureMut<'env, (Marker1, Marker2)> for F -where - F: ReflectFnMut<'env, Marker1> + TypedFunction + 'env, -{ - fn into_closure_mut(mut self) -> DynamicClosureMut<'env> { - DynamicClosureMut::new( - move |args| self.reflect_call_mut(args), - Self::function_info(), - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::func::{ArgList, IntoClosure}; - - #[test] - fn should_create_dynamic_closure_mut_from_closure() { - let c = 23; - let func = (|a: i32, b: i32| a + b + c).into_closure(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.try_downcast_ref::(), Some(&123)); - } - - #[test] - fn should_create_dynamic_closure_mut_from_closure_with_mutable_capture() { - let mut total = 0; - let func = (|a: i32, b: i32| total = a + b).into_closure_mut(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - func.call_once(args).unwrap(); - assert_eq!(total, 100); - } - - #[test] - fn should_create_dynamic_closure_mut_from_function() { - fn add(a: i32, b: i32) -> i32 { - a + b - } - - let mut func = add.into_closure_mut(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.try_downcast_ref::(), Some(&100)); - } - - #[test] - fn should_default_closure_name_to_none() { - let mut total = 0; - let func = (|a: i32, b: i32| total = a + b).into_closure_mut(); - assert_eq!(func.info().name(), None); - } -} diff --git a/crates/bevy_reflect/src/func/closures/mod.rs b/crates/bevy_reflect/src/func/closures/mod.rs deleted file mode 100644 index cdba3bab06..0000000000 --- a/crates/bevy_reflect/src/func/closures/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub use dynamic_closure::*; -pub use dynamic_closure_mut::*; -pub use into_closure::*; -pub use into_closure_mut::*; - -mod dynamic_closure; -mod dynamic_closure_mut; -mod into_closure; -mod into_closure_mut; diff --git a/crates/bevy_reflect/src/func/dynamic_function.rs b/crates/bevy_reflect/src/func/dynamic_function.rs new file mode 100644 index 0000000000..2794f1cb9a --- /dev/null +++ b/crates/bevy_reflect/src/func/dynamic_function.rs @@ -0,0 +1,232 @@ +use crate::func::args::{ArgInfo, ArgList}; +use crate::func::info::FunctionInfo; +use crate::func::{DynamicFunctionMut, FunctionResult, IntoFunction, IntoFunctionMut, ReturnInfo}; +use alloc::borrow::Cow; +use core::fmt::{Debug, Formatter}; +use std::sync::Arc; + +/// A dynamic representation of a function. +/// +/// This type can be used to represent any callable that satisfies [`Fn`] +/// (or the reflection-based equivalent, [`ReflectFn`]). +/// That is, any function or closure that does not mutably borrow data from its environment. +/// +/// For functions that do need to capture their environment mutably (i.e. mutable closures), +/// see [`DynamicFunctionMut`]. +/// +/// See the [module-level documentation] for more information. +/// +/// You will generally not need to construct this manually. +/// Instead, many functions and closures can be automatically converted using the [`IntoFunction`] trait. +/// +/// # Example +/// +/// Most of the time, a [`DynamicFunction`] can be created using the [`IntoFunction`] trait: +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, DynamicFunction, FunctionInfo, IntoFunction}; +/// # +/// fn add(a: i32, b: i32) -> i32 { +/// a + b +/// } +/// +/// // Convert the function into a dynamic function using `IntoFunction::into_function`: +/// let mut func: DynamicFunction = add.into_function(); +/// +/// // Dynamically call it: +/// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); +/// let value = func.call(args).unwrap().unwrap_owned(); +/// +/// // Check the result: +/// assert_eq!(value.try_downcast_ref::(), Some(&100)); +/// ``` +/// +/// [`ReflectFn`]: crate::func::ReflectFn +/// [module-level documentation]: crate::func +pub struct DynamicFunction<'env> { + pub(super) info: FunctionInfo, + pub(super) func: Arc Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>, +} + +impl<'env> DynamicFunction<'env> { + /// Create a new [`DynamicFunction`]. + /// + /// The given function can be used to call out to any other callable, + /// including functions, closures, or methods. + /// + /// It's important that the function signature matches the provided [`FunctionInfo`]. + /// This info may be used by consumers of this function for validation and debugging. + pub fn new Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>( + func: F, + info: FunctionInfo, + ) -> Self { + Self { + info, + func: Arc::new(func), + } + } + + /// Set the name of the function. + /// + /// For [`DynamicFunctions`] created using [`IntoFunction`], + /// the default name will always be the full path to the function as returned by [`std::any::type_name`], + /// unless the function is a closure, anonymous function, or function pointer, + /// in which case the name will be `None`. + /// + /// [`DynamicFunctions`]: DynamicFunction + pub fn with_name(mut self, name: impl Into>) -> Self { + self.info = self.info.with_name(name); + self + } + + /// Set the argument information of the function. + /// + /// It's important that the arguments match the intended function signature, + /// as this can be used by consumers of this function for validation and debugging. + pub fn with_args(mut self, args: Vec) -> Self { + self.info = self.info.with_args(args); + self + } + + /// Set the return information of the function. + pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { + self.info = self.info.with_return_info(return_info); + self + } + + /// Call the function with the given arguments. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::{IntoFunction, ArgList}; + /// let c = 23; + /// let add = |a: i32, b: i32| -> i32 { + /// a + b + c + /// }; + /// + /// let mut func = add.into_function().with_name("add"); + /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.try_take::().unwrap(), 123); + /// ``` + pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> { + (self.func)(args) + } + + /// Returns the function info. + pub fn info(&self) -> &FunctionInfo { + &self.info + } + + /// The [name] of the function. + /// + /// For [`DynamicFunctions`] created using [`IntoFunction`], + /// the default name will always be the full path to the function as returned by [`std::any::type_name`], + /// unless the function is a closure, anonymous function, or function pointer, + /// in which case the name will be `None`. + /// + /// This can be overridden using [`with_name`]. + /// + /// [name]: FunctionInfo::name + /// [`DynamicFunctions`]: DynamicFunction + /// [`with_name`]: Self::with_name + pub fn name(&self) -> Option<&Cow<'static, str>> { + self.info.name() + } +} + +/// Outputs the function's signature. +/// +/// This takes the format: `DynamicFunction(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. +/// +/// Names for arguments and the function itself are optional and will default to `_` if not provided. +impl<'env> Debug for DynamicFunction<'env> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let name = self.info.name().unwrap_or(&Cow::Borrowed("_")); + write!(f, "DynamicFunction(fn {name}(")?; + + for (index, arg) in self.info.args().iter().enumerate() { + let name = arg.name().unwrap_or("_"); + let ty = arg.type_path(); + write!(f, "{name}: {ty}")?; + + if index + 1 < self.info.args().len() { + write!(f, ", ")?; + } + } + + let ret = self.info.return_info().type_path(); + write!(f, ") -> {ret})") + } +} + +impl<'env> Clone for DynamicFunction<'env> { + fn clone(&self) -> Self { + Self { + info: self.info.clone(), + func: Arc::clone(&self.func), + } + } +} + +impl<'env> IntoFunction<'env, ()> for DynamicFunction<'env> { + #[inline] + fn into_function(self) -> DynamicFunction<'env> { + self + } +} + +impl<'env> IntoFunctionMut<'env, ()> for DynamicFunction<'env> { + #[inline] + fn into_function_mut(self) -> DynamicFunctionMut<'env> { + DynamicFunctionMut::from(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_overwrite_function_name() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c) + .into_function() + .with_name("my_function"); + assert_eq!(func.info().name().unwrap(), "my_function"); + } + + #[test] + fn should_convert_dynamic_function_with_into_function() { + fn make_closure<'env, F: IntoFunction<'env, M>, M>(f: F) -> DynamicFunction<'env> { + f.into_function() + } + + let c = 23; + let function: DynamicFunction = make_closure(|a: i32, b: i32| a + b + c); + let _: DynamicFunction = make_closure(function); + } + + #[test] + fn should_clone_dynamic_function() { + let hello = String::from("Hello"); + + let greet = |name: &String| -> String { format!("{}, {}!", hello, name) }; + + let greet = greet.into_function().with_name("greet"); + let clone = greet.clone(); + + assert_eq!(greet.name().unwrap(), "greet"); + assert_eq!(clone.name().unwrap(), "greet"); + + let clone_value = clone + .call(ArgList::default().push_ref(&String::from("world"))) + .unwrap() + .unwrap_owned() + .try_take::() + .unwrap(); + + assert_eq!(clone_value, "Hello, world!"); + } +} diff --git a/crates/bevy_reflect/src/func/dynamic_function_mut.rs b/crates/bevy_reflect/src/func/dynamic_function_mut.rs new file mode 100644 index 0000000000..06d1e0f71c --- /dev/null +++ b/crates/bevy_reflect/src/func/dynamic_function_mut.rs @@ -0,0 +1,255 @@ +use alloc::borrow::Cow; +use core::fmt::{Debug, Formatter}; + +use crate::func::args::{ArgInfo, ArgList}; +use crate::func::info::FunctionInfo; +use crate::func::{DynamicFunction, FunctionResult, IntoFunctionMut, ReturnInfo}; + +/// A dynamic representation of a function. +/// +/// This type can be used to represent any callable that satisfies [`FnMut`] +/// (or the reflection-based equivalent, [`ReflectFnMut`]). +/// That is, any function or closure. +/// +/// For functions that do not need to capture their environment mutably, +/// it's recommended to use [`DynamicFunction`] instead. +/// +/// This type can be seen as a superset of [`DynamicFunction`]. +/// +/// See the [module-level documentation] for more information. +/// +/// You will generally not need to construct this manually. +/// Instead, many functions and closures can be automatically converted using the [`IntoFunctionMut`] trait. +/// +/// # Example +/// +/// Most of the time, a [`DynamicFunctionMut`] can be created using the [`IntoFunctionMut`] trait: +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, DynamicFunctionMut, FunctionInfo, IntoFunctionMut}; +/// # +/// let mut list: Vec = vec![1, 2, 3]; +/// +/// // `replace` is a closure that captures a mutable reference to `list` +/// let mut replace = |index: usize, value: i32| -> i32 { +/// let old_value = list[index]; +/// list[index] = value; +/// old_value +/// }; +/// +/// // Since this closure mutably borrows data, we can't convert it into a regular `DynamicFunction`, +/// // as doing so would result in a compile-time error: +/// // let mut func: DynamicFunction = replace.into_function(); +/// +/// // Instead, we convert it into a `DynamicFunctionMut` using `IntoFunctionMut::into_function_mut`: +/// let mut func: DynamicFunctionMut = replace.into_function_mut(); +/// +/// // Dynamically call it: +/// let args = ArgList::default().push_owned(1_usize).push_owned(-2_i32); +/// let value = func.call(args).unwrap().unwrap_owned(); +/// +/// // Check the result: +/// assert_eq!(value.try_take::().unwrap(), 2); +/// +/// // Note that `func` still has a reference to `list`, +/// // so we need to drop it before we can access `list` again. +/// // Alternatively, we could have invoked `func` with +/// // `DynamicFunctionMut::call_once` to immediately consume it. +/// drop(func); +/// assert_eq!(list, vec![1, -2, 3]); +/// ``` +/// +/// [`ReflectFnMut`]: crate::func::ReflectFnMut +/// [module-level documentation]: crate::func +pub struct DynamicFunctionMut<'env> { + info: FunctionInfo, + func: Box FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>, +} + +impl<'env> DynamicFunctionMut<'env> { + /// Create a new [`DynamicFunctionMut`]. + /// + /// The given function can be used to call out to any other callable, + /// including functions, closures, or methods. + /// + /// It's important that the function signature matches the provided [`FunctionInfo`]. + /// This info may be used by consumers of this function for validation and debugging. + pub fn new FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>( + func: F, + info: FunctionInfo, + ) -> Self { + Self { + info, + func: Box::new(func), + } + } + + /// Set the name of the function. + /// + /// For [`DynamicFunctionMuts`] created using [`IntoFunctionMut`], + /// the default name will always be the full path to the function as returned by [`std::any::type_name`], + /// unless the function is a closure, anonymous function, or function pointer, + /// in which case the name will be `None`. + /// + /// [`DynamicFunctionMuts`]: DynamicFunctionMut + pub fn with_name(mut self, name: impl Into>) -> Self { + self.info = self.info.with_name(name); + self + } + + /// Set the argument information of the function. + /// + /// It's important that the arguments match the intended function signature, + /// as this can be used by consumers of this function for validation and debugging. + pub fn with_args(mut self, args: Vec) -> Self { + self.info = self.info.with_args(args); + self + } + + /// Set the return information of the function. + pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { + self.info = self.info.with_return_info(return_info); + self + } + + /// Call the function with the given arguments. + /// + /// Variables that are captured mutably by this function + /// won't be usable until this function is dropped. + /// Consider using [`call_once`] if you want to consume the function + /// immediately after calling it. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::{IntoFunctionMut, ArgList}; + /// let mut total = 0; + /// let add = |a: i32, b: i32| -> i32 { + /// total = a + b; + /// total + /// }; + /// + /// let mut func = add.into_function_mut().with_name("add"); + /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.try_take::().unwrap(), 100); + /// ``` + /// + /// [`call_once`]: DynamicFunctionMut::call_once + pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> { + (self.func)(args) + } + + /// Call the function with the given arguments and consume it. + /// + /// This is useful for functions that capture their environment mutably + /// because otherwise any captured variables would still be borrowed by it. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::{IntoFunctionMut, ArgList}; + /// let mut count = 0; + /// let increment = |amount: i32| count += amount; + /// + /// let increment_function = increment.into_function_mut(); + /// let args = ArgList::new().push_owned(5_i32); + /// + /// // We need to drop `increment_function` here so that we + /// // can regain access to `count`. + /// // `call_once` does this automatically for us. + /// increment_function.call_once(args).unwrap(); + /// assert_eq!(count, 5); + /// ``` + pub fn call_once(mut self, args: ArgList) -> FunctionResult { + (self.func)(args) + } + + /// Returns the function info. + pub fn info(&self) -> &FunctionInfo { + &self.info + } + + /// The [name] of the function. + /// + /// For [`DynamicFunctionMuts`] created using [`IntoFunctionMut`], + /// the default name will always be the full path to the function as returned by [`std::any::type_name`], + /// unless the function is a closure, anonymous function, or function pointer, + /// in which case the name will be `None`. + /// + /// This can be overridden using [`with_name`]. + /// + /// [name]: FunctionInfo::name + /// [`DynamicFunctionMuts`]: DynamicFunctionMut + /// [`with_name`]: Self::with_name + pub fn name(&self) -> Option<&Cow<'static, str>> { + self.info.name() + } +} + +/// Outputs the function's signature. +/// +/// This takes the format: `DynamicFunctionMut(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. +/// +/// Names for arguments and the function itself are optional and will default to `_` if not provided. +impl<'env> Debug for DynamicFunctionMut<'env> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let name = self.info.name().unwrap_or(&Cow::Borrowed("_")); + write!(f, "DynamicFunctionMut(fn {name}(")?; + + for (index, arg) in self.info.args().iter().enumerate() { + let name = arg.name().unwrap_or("_"); + let ty = arg.type_path(); + write!(f, "{name}: {ty}")?; + + if index + 1 < self.info.args().len() { + write!(f, ", ")?; + } + } + + let ret = self.info.return_info().type_path(); + write!(f, ") -> {ret})") + } +} + +impl<'env> From> for DynamicFunctionMut<'env> { + #[inline] + fn from(function: DynamicFunction<'env>) -> Self { + Self { + info: function.info, + func: Box::new(move |args| (function.func)(args)), + } + } +} + +impl<'env> IntoFunctionMut<'env, ()> for DynamicFunctionMut<'env> { + #[inline] + fn into_function_mut(self) -> DynamicFunctionMut<'env> { + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_overwrite_function_name() { + let mut total = 0; + let func = (|a: i32, b: i32| total = a + b) + .into_function_mut() + .with_name("my_function"); + assert_eq!(func.info().name().unwrap(), "my_function"); + } + + #[test] + fn should_convert_dynamic_function_mut_with_into_function() { + fn make_closure<'env, F: IntoFunctionMut<'env, M>, M>(f: F) -> DynamicFunctionMut<'env> { + f.into_function_mut() + } + + let mut total = 0; + let closure: DynamicFunctionMut = make_closure(|a: i32, b: i32| total = a + b); + let _: DynamicFunctionMut = make_closure(closure); + } +} diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index 30ef98b0a8..a37123f1b6 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -3,10 +3,10 @@ use crate::func::Return; use alloc::borrow::Cow; use thiserror::Error; -/// An error that occurs when calling a [`DynamicFunction`] or [`DynamicClosure`]. +/// An error that occurs when calling a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction -/// [`DynamicClosure`]: crate::func::DynamicClosure +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug, Error, PartialEq)] pub enum FunctionError { /// An error occurred while converting an argument. @@ -17,13 +17,13 @@ pub enum FunctionError { ArgCountMismatch { expected: usize, received: usize }, } -/// The result of calling a dynamic [`DynamicFunction`] or [`DynamicClosure`]. +/// The result of calling a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// Returns `Ok(value)` if the function was called successfully, /// where `value` is the [`Return`] value of the function. /// /// [`DynamicFunction`]: crate::func::DynamicFunction -/// [`DynamicClosure`]: crate::func::DynamicClosure +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut pub type FunctionResult<'a> = Result, FunctionError>; /// An error that occurs when registering a function into a [`FunctionRegistry`]. diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs deleted file mode 100644 index 28c1ae3ad8..0000000000 --- a/crates/bevy_reflect/src/func/function.rs +++ /dev/null @@ -1,292 +0,0 @@ -use alloc::borrow::Cow; -use core::fmt::{Debug, Formatter}; -use std::sync::Arc; - -use crate::func::args::{ArgInfo, ArgList}; -use crate::func::info::FunctionInfo; -use crate::func::{ - DynamicClosure, DynamicClosureMut, FunctionResult, IntoClosure, IntoClosureMut, IntoFunction, - ReturnInfo, -}; - -/// A dynamic representation of a Rust function. -/// -/// For our purposes, a "function" is just a callable that may not reference its environment. -/// -/// This includes: -/// - Functions and methods defined with the `fn` keyword -/// - Closures that do not capture their environment -/// - Closures that take ownership of captured variables -/// -/// To handle closures that capture references to their environment, see [`DynamicClosure`]. -/// -/// See the [module-level documentation] for more information. -/// -/// You will generally not need to construct this manually. -/// Instead, many functions and closures can be automatically converted using the [`IntoFunction`] trait. -/// -/// # Example -/// -/// Most of the time, a [`DynamicFunction`] can be created using the [`IntoFunction`] trait: -/// -/// ``` -/// # use bevy_reflect::func::args::ArgList; -/// # use bevy_reflect::func::{DynamicFunction, IntoFunction}; -/// fn add(a: i32, b: i32) -> i32 { -/// a + b -/// } -/// -/// // Convert the function into a dynamic function using `IntoFunction::into_function` -/// let mut func: DynamicFunction = add.into_function(); -/// -/// // Dynamically call the function: -/// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); -/// let value = func.call(args).unwrap().unwrap_owned(); -/// -/// // Check the result: -/// assert_eq!(value.try_downcast_ref::(), Some(&100)); -/// ``` -/// -/// However, in some cases, these functions may need to be created manually: -/// -/// ``` -/// # use bevy_reflect::func::{ArgList, DynamicFunction, FunctionInfo, IntoFunction, Return, ReturnInfo}; -/// # use bevy_reflect::func::args::ArgInfo; -/// fn append(value: String, list: &mut Vec) -> &mut String { -/// list.push(value); -/// list.last_mut().unwrap() -/// } -/// -/// // Due to the return value being a reference that is not tied to the first argument, -/// // this will fail to compile: -/// // let mut func: DynamicFunction = append.into_function(); -/// -/// // Instead, we need to define the function manually. -/// // We start by defining the shape of the function: -/// let info = FunctionInfo::named("append") -/// .with_arg::("value") -/// .with_arg::<&mut Vec>("list") -/// .with_return::<&mut String>(); -/// -/// // Then we define the dynamic function, which will be used to call our `append` function: -/// let mut func = DynamicFunction::new(|mut args| { -/// // Arguments are removed from the list in order: -/// let arg0 = args.take::()?; -/// let arg1 = args.take::<&mut Vec>()?; -/// -/// // Then we can call our function and return the result: -/// Ok(Return::Mut(append(arg0, arg1))) -/// }, info); -/// -/// let mut list = Vec::::new(); -/// -/// // Dynamically call the function: -/// let args = ArgList::default().push_owned("Hello, World".to_string()).push_mut(&mut list); -/// let value = func.call(args).unwrap().unwrap_mut(); -/// -/// // Mutate the return value: -/// value.try_downcast_mut::().unwrap().push_str("!!!"); -/// -/// // Check the result: -/// assert_eq!(list, vec!["Hello, World!!!"]); -/// ``` -/// -/// [module-level documentation]: crate::func -pub struct DynamicFunction { - pub(super) info: FunctionInfo, - pub(super) func: Arc Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'static>, -} - -impl DynamicFunction { - /// Create a new dynamic [`DynamicFunction`]. - /// - /// The given function can be used to call out to a regular function, closure, or method. - /// - /// It's important that the function signature matches the provided [`FunctionInfo`]. - /// This info may be used by consumers of the function for validation and debugging. - pub fn new Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'static>( - func: F, - info: FunctionInfo, - ) -> Self { - Self { - info, - func: Arc::new(func), - } - } - - /// Set the name of the function. - /// - /// For [`DynamicFunctions`] created using [`IntoFunction`], - /// the default name will always be the full path to the function as returned by [`std::any::type_name`]. - /// - /// [`DynamicFunctions`]: DynamicFunction - pub fn with_name(mut self, name: impl Into>) -> Self { - self.info = self.info.with_name(name); - self - } - - /// Set the arguments of the function. - /// - /// It's important that the arguments match the intended function signature, - /// as this can be used by consumers of the function for validation and debugging. - pub fn with_args(mut self, args: Vec) -> Self { - self.info = self.info.with_args(args); - self - } - - /// Set the return information of the function. - pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { - self.info = self.info.with_return_info(return_info); - self - } - - /// Call the function with the given arguments. - /// - /// # Example - /// - /// ``` - /// # use bevy_reflect::func::{IntoFunction, ArgList}; - /// fn add(a: i32, b: i32) -> i32 { - /// a + b - /// } - /// - /// let func = add.into_function(); - /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - /// let result = func.call(args).unwrap().unwrap_owned(); - /// assert_eq!(result.try_take::().unwrap(), 100); - /// ``` - pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> { - (self.func)(args) - } - - /// Returns the function info. - pub fn info(&self) -> &FunctionInfo { - &self.info - } - - /// The [name] of the function. - /// - /// If this [`DynamicFunction`] was created using [`IntoFunction`], - /// then the name will default to one of the following: - /// - If the function was anonymous (e.g. `|a: i32, b: i32| { a + b }`), - /// then the name will be `None` - /// - If the function was named (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`), - /// then the name will be the full path to the function as returned by [`std::any::type_name`]. - /// - /// This can be overridden using [`with_name`]. - /// - /// [name]: FunctionInfo::name - /// [`with_name`]: Self::with_name - pub fn name(&self) -> Option<&Cow<'static, str>> { - self.info.name() - } -} - -/// Outputs the function signature. -/// -/// This takes the format: `DynamicFunction(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. -/// -/// Names for arguments are optional and will default to `_` if not provided. -impl Debug for DynamicFunction { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let name = self.info.name().unwrap_or(&Cow::Borrowed("_")); - write!(f, "DynamicFunction(fn {name}(")?; - - for (index, arg) in self.info.args().iter().enumerate() { - let name = arg.name().unwrap_or("_"); - let ty = arg.type_path(); - write!(f, "{name}: {ty}")?; - - if index + 1 < self.info.args().len() { - write!(f, ", ")?; - } - } - - let ret = self.info.return_info().type_path(); - write!(f, ") -> {ret})") - } -} - -impl Clone for DynamicFunction { - fn clone(&self) -> Self { - Self { - info: self.info.clone(), - func: Arc::clone(&self.func), - } - } -} - -impl IntoFunction<()> for DynamicFunction { - #[inline] - fn into_function(self) -> DynamicFunction { - self - } -} - -impl IntoClosure<'_, ()> for DynamicFunction { - #[inline] - fn into_closure(self) -> DynamicClosure<'static> { - DynamicClosure::from(self) - } -} - -impl IntoClosureMut<'_, ()> for DynamicFunction { - #[inline] - fn into_closure_mut(self) -> DynamicClosureMut<'static> { - DynamicClosureMut::from(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::func::Return; - - #[test] - fn should_overwrite_function_name() { - fn foo() {} - - let func = foo.into_function().with_name("my_function"); - assert_eq!(func.info().name().unwrap(), "my_function"); - } - - #[test] - fn should_convert_dynamic_function_with_into_function() { - fn make_function, M>(f: F) -> DynamicFunction { - f.into_function() - } - - let function: DynamicFunction = make_function(|| {}); - let _: DynamicFunction = make_function(function); - } - - #[test] - fn should_allow_manual_function_construction() { - #[allow(clippy::ptr_arg)] - fn get(index: usize, list: &Vec) -> &String { - &list[index] - } - - let func = DynamicFunction::new( - |mut args| { - let list = args.pop::<&Vec>()?; - let index = args.pop::()?; - Ok(Return::Ref(get(index, list))) - }, - FunctionInfo::anonymous() - .with_name("get") - .with_arg::("index") - .with_arg::<&Vec>("list") - .with_return::<&String>(), - ); - - let list = vec![String::from("foo")]; - let value = func - .call(ArgList::new().push_owned(0_usize).push_ref(&list)) - .unwrap() - .unwrap_ref() - .try_downcast_ref::() - .unwrap(); - assert_eq!(value, "foo"); - } -} diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 6b4c03ed6a..413af8a3ed 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -5,13 +5,13 @@ use bevy_utils::all_tuples; use crate::func::args::{ArgInfo, GetOwnership, Ownership}; use crate::TypePath; -/// Type information for a [`DynamicFunction`] or [`DynamicClosure`]. +/// Type information for a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// -/// This information can be retrieved from certain functions and closures -/// using the [`TypedFunction`] trait. +/// This information can be retrieved directly from certain functions and closures +/// using the [`TypedFunction`] trait, and manually constructed otherwise. /// /// [`DynamicFunction`]: crate::func::DynamicFunction -/// [`DynamicClosure`]: crate::func::DynamicClosure +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug, Clone)] pub struct FunctionInfo { name: Option>, @@ -43,7 +43,7 @@ impl FunctionInfo { } } - /// Create a new [`FunctionInfo`] from the given function or closure. + /// Create a new [`FunctionInfo`] from the given function. pub fn from(function: &F) -> Self where F: TypedFunction, @@ -105,13 +105,15 @@ impl FunctionInfo { /// The name of the function. /// - /// For [`DynamicFunctions`] created using [`IntoFunction`] or [`DynamicClosures`] created using [`IntoClosure`], - /// the name will always be the full path to the function as returned by [`std::any::type_name`]. + /// For [`DynamicFunctions`] created using [`IntoFunction`] or [`DynamicFunctionMuts`] created using [`IntoFunctionMut`], + /// the default name will always be the full path to the function as returned by [`std::any::type_name`], + /// unless the function is a closure, anonymous function, or function pointer, + /// in which case the name will be `None`. /// /// [`DynamicFunctions`]: crate::func::DynamicFunction /// [`IntoFunction`]: crate::func::IntoFunction - /// [`DynamicClosures`]: crate::func::DynamicClosure - /// [`IntoClosure`]: crate::func::IntoClosure + /// [`DynamicFunctionMuts`]: crate::func::DynamicFunctionMut + /// [`IntoFunctionMut`]: crate::func::IntoFunctionMut pub fn name(&self) -> Option<&Cow<'static, str>> { self.name.as_ref() } @@ -132,10 +134,10 @@ impl FunctionInfo { } } -/// Information about the return type of a [`DynamicFunction`] or [`DynamicClosure`]. +/// Information about the return type of a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction -/// [`DynamicClosure`]: crate::func::DynamicClosure +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug, Clone)] pub struct ReturnInfo { type_path: &'static str, @@ -164,13 +166,14 @@ impl ReturnInfo { /// A static accessor to compile-time type information for functions. /// -/// This is the equivalent of [`Typed`] for functions. +/// This is the equivalent of [`Typed`], but for function. /// /// # Blanket Implementation /// /// This trait has a blanket implementation that covers: /// - Functions and methods defined with the `fn` keyword -/// - Closures that do not capture their environment +/// - Anonymous functions +/// - Function pointers /// - Closures that capture immutable references to their environment /// - Closures that capture mutable references to their environment /// - Closures that take ownership of captured variables @@ -209,6 +212,7 @@ impl ReturnInfo { /// /// [module-level documentation]: crate::func /// [`Typed`]: crate::Typed +/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html pub trait TypedFunction { /// Get the [`FunctionInfo`] for this type. fn function_info() -> FunctionInfo; @@ -219,7 +223,7 @@ pub trait TypedFunction { } } -/// Helper macro for implementing [`TypedFunction`] on Rust closures. +/// Helper macro for implementing [`TypedFunction`] on Rust functions. /// /// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`): /// - `FnMut(arg0, arg1, ..., argN) -> R` diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 965661aa7a..3b52605aa3 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -1,52 +1,34 @@ -use std::panic::{RefUnwindSafe, UnwindSafe}; - -use crate::func::function::DynamicFunction; -use crate::func::{ReflectFn, TypedFunction}; +use crate::func::{DynamicFunction, ReflectFn, TypedFunction}; /// A trait for types that can be converted into a [`DynamicFunction`]. /// -/// This trait is automatically implemented for many standard Rust functions -/// that also implement [`ReflectFn`] and [`TypedFunction`]. -/// -/// To handle types such as closures that capture references to their environment, -/// see [`IntoClosure`] instead. +/// This trait is automatically implemented for any type that implements +/// [`ReflectFn`] and [`TypedFunction`]. /// /// See the [module-level documentation] for more information. /// -/// [`IntoClosure`]: crate::func::IntoClosure +/// # Trait Parameters +/// +/// This trait has a `Marker` type parameter that is used to get around issues with +/// [unconstrained type parameters] when defining impls with generic arguments or return types. +/// This `Marker` can be any type, provided it doesn't conflict with other implementations. +/// +/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function. +/// For named functions and some closures, this will end up just being `'static`, +/// however, closures that borrow from their environment will have a lifetime bound to that environment. +/// /// [module-level documentation]: crate::func -pub trait IntoFunction { +/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html +pub trait IntoFunction<'env, Marker> { /// Converts [`Self`] into a [`DynamicFunction`]. - fn into_function(self) -> DynamicFunction; + fn into_function(self) -> DynamicFunction<'env>; } -impl IntoFunction<(Marker1, Marker2)> for F +impl<'env, F, Marker1, Marker2> IntoFunction<'env, (Marker1, Marker2)> for F where - F: ReflectFn<'static, Marker1> - + TypedFunction - // Ideally, we'd only implement `IntoFunction` on actual function types - // (i.e. functions that do not capture their environment at all), - // but this would only work if users first explicitly coerced their functions - // to a function pointer like `(add as fn(i32, i32) -> i32).into_function()`, - // which is certainly not the best user experience. - // So as a compromise, we'll stick to allowing any type that implements - // `ReflectFn` and `TypedFunction`, but also add the following trait bounds - // that all `fn` types implement: - + Clone - + Copy - + Send - + Sync - + Unpin - + UnwindSafe - + RefUnwindSafe - + 'static, + F: ReflectFn<'env, Marker1> + TypedFunction + Send + Sync + 'env, { - fn into_function(self) -> DynamicFunction { - // Note that to further guarantee that `self` is a true `fn` type, - // we could add a compile time assertion that `F` is zero-sized. - // However, we don't do this because it would prevent users from - // converting function pointers into `DynamicFunction`s. - + fn into_function(self) -> DynamicFunction<'env> { DynamicFunction::new(move |args| self.reflect_call(args), Self::function_info()) } } @@ -54,9 +36,16 @@ where #[cfg(test)] mod tests { use super::*; - use crate as bevy_reflect; use crate::func::ArgList; - use bevy_reflect_derive::Reflect; + + #[test] + fn should_create_dynamic_function_from_closure() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c).into_function(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_downcast_ref::(), Some(&123)); + } #[test] fn should_create_dynamic_function_from_function() { @@ -71,110 +60,9 @@ mod tests { } #[test] - fn should_create_dynamic_function_from_function_pointer() { - fn add(a: i32, b: i32) -> i32 { - a + b - } - - let func = (add as fn(i32, i32) -> i32).into_function(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.try_downcast_ref::(), Some(&100)); - } - - #[test] - fn should_create_dynamic_function_from_anonymous_function() { - let func = (|a: i32, b: i32| a + b).into_function(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.try_downcast_ref::(), Some(&100)); - } - - #[test] - fn should_create_dynamic_function_from_method() { - #[derive(Reflect, Debug, PartialEq)] - struct Foo(i32); - - impl Foo { - pub fn add(&self, other: &Foo) -> Foo { - Foo(self.0 + other.0) - } - } - - let foo_a = Foo(25); - let foo_b = Foo(75); - - let func = Foo::add.into_function(); - let args = ArgList::new().push_ref(&foo_a).push_ref(&foo_b); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.try_downcast_ref::(), Some(&Foo(100))); - } - - #[test] - fn should_allow_zero_args() { - fn foo() -> String { - String::from("Hello, World!") - } - - let func = foo.into_function(); - let args = ArgList::new(); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!( - result.try_downcast_ref::(), - Some(&String::from("Hello, World!")) - ); - } - - #[test] - fn should_allow_unit_return() { - fn foo(_: i32) {} - - let func = foo.into_function(); - let args = ArgList::new().push_owned(123_i32); - let result = func.call(args).unwrap(); - assert!(result.is_unit()); - } - - #[test] - fn should_allow_reference_return() { - fn foo<'a>(value: &'a i32, _: String, _: &bool) -> &'a i32 { - value - } - - let value: i32 = 123; - let func = foo.into_function(); - let args = ArgList::new() - .push_ref(&value) - .push_owned(String::from("Hello, World!")) - .push_ref(&true); - let result = func.call(args).unwrap().unwrap_ref(); - assert_eq!(result.try_downcast_ref::(), Some(&123)); - } - - #[test] - fn should_allow_mutable_reference_return() { - fn foo<'a>(value: &'a mut i32, _: String, _: &bool) -> &'a mut i32 { - value - } - - let mut value: i32 = 123; - let func = foo.into_function(); - let args = ArgList::new() - .push_mut(&mut value) - .push_owned(String::from("Hello, World!")) - .push_ref(&true); - let result = func.call(args).unwrap().unwrap_mut(); - assert_eq!(result.try_downcast_mut::(), Some(&mut 123)); - } - - #[test] - fn should_default_with_function_type_name() { - fn foo() {} - - let func = foo.into_function(); - assert_eq!( - func.info().name().unwrap(), - "bevy_reflect::func::into_function::tests::should_default_with_function_type_name::foo" - ); + fn should_default_closure_name_to_none() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c).into_function(); + assert_eq!(func.info().name(), None); } } diff --git a/crates/bevy_reflect/src/func/into_function_mut.rs b/crates/bevy_reflect/src/func/into_function_mut.rs new file mode 100644 index 0000000000..b4671047a6 --- /dev/null +++ b/crates/bevy_reflect/src/func/into_function_mut.rs @@ -0,0 +1,83 @@ +use crate::func::{DynamicFunctionMut, ReflectFnMut, TypedFunction}; + +/// A trait for types that can be converted into a [`DynamicFunctionMut`]. +/// +/// This trait is automatically implemented for any type that implements +/// [`ReflectFnMut`] and [`TypedFunction`]. +/// +/// This trait can be seen as a superset of [`IntoFunction`]. +/// +/// See the [module-level documentation] for more information. +/// +/// # Trait Parameters +/// +/// This trait has a `Marker` type parameter that is used to get around issues with +/// [unconstrained type parameters] when defining impls with generic arguments or return types. +/// This `Marker` can be any type, provided it doesn't conflict with other implementations. +/// +/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function. +/// For named functions and some closures, this will end up just being `'static`, +/// however, closures that borrow from their environment will have a lifetime bound to that environment. +/// +/// [`IntoFunction`]: crate::func::IntoFunction +/// [module-level documentation]: crate::func +/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html +pub trait IntoFunctionMut<'env, Marker> { + /// Converts [`Self`] into a [`DynamicFunctionMut`]. + fn into_function_mut(self) -> DynamicFunctionMut<'env>; +} + +impl<'env, F, Marker1, Marker2> IntoFunctionMut<'env, (Marker1, Marker2)> for F +where + F: ReflectFnMut<'env, Marker1> + TypedFunction + 'env, +{ + fn into_function_mut(mut self) -> DynamicFunctionMut<'env> { + DynamicFunctionMut::new( + move |args| self.reflect_call_mut(args), + Self::function_info(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::func::{ArgList, IntoFunction}; + + #[test] + fn should_create_dynamic_function_mut_from_closure() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c).into_function(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_downcast_ref::(), Some(&123)); + } + + #[test] + fn should_create_dynamic_function_mut_from_closure_with_mutable_capture() { + let mut total = 0; + let func = (|a: i32, b: i32| total = a + b).into_function_mut(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + func.call_once(args).unwrap(); + assert_eq!(total, 100); + } + + #[test] + fn should_create_dynamic_function_mut_from_function() { + fn add(a: i32, b: i32) -> i32 { + a + b + } + + let mut func = add.into_function_mut(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_downcast_ref::(), Some(&100)); + } + + #[test] + fn should_default_closure_name_to_none() { + let mut total = 0; + let func = (|a: i32, b: i32| total = a + b).into_function_mut(); + assert_eq!(func.info().name(), None); + } +} diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 172c1a0409..a924231ff4 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -1,10 +1,10 @@ //! Reflection-based dynamic functions. //! //! This module provides a way to pass around and call functions dynamically -//! using the [`DynamicFunction`], [`DynamicClosure`], and [`DynamicClosureMut`] types. +//! using the [`DynamicFunction`] and [`DynamicFunctionMut`] types. //! //! Many simple functions and closures can be automatically converted to these types -//! using the [`IntoFunction`], [`IntoClosure`], and [`IntoClosureMut`] traits, respectively. +//! using the [`IntoFunction`] and [`IntoFunctionMut`] traits, respectively. //! //! Once this dynamic representation is created, it can be called with a set of arguments provided //! via an [`ArgList`]. @@ -33,22 +33,28 @@ //! assert_eq!(value.unwrap_owned().try_downcast_ref::(), Some(&100)); //! ``` //! -//! # Functions vs Closures +//! # Types of Functions //! -//! In Rust, a "function" is any callable that does not capture its environment. -//! These are typically defined with the `fn` keyword, but may also use anonymous function syntax. +//! For simplicity, this module uses the umbrella term "function" to refer to any Rust callable: +//! code that can be invoked with a set of arguments to perform some action. +//! +//! In Rust, there are two main categories of callables: functions and closures. +//! +//! A "function" is a callable that does not capture its environment. +//! These are typically defined with the `fn` keyword, which are referred to as _named_ functions. +//! But they are also _anonymous_ functions, which are unnamed and defined with anonymous function syntax. //! //! ```rust -//! // This is a standard Rust function: +//! // This is a named function: //! fn add(a: i32, b: i32) -> i32 { //! a + b //! } //! -//! // This is an anonymous Rust function: +//! // This is an anonymous function: //! let add = |a: i32, b: i32| a + b; //! ``` //! -//! Rust also has the concept of "closures", which are special functions that capture their environment. +//! Closures, on the other hand, are special functions that do capture their environment. //! These are always defined with anonymous function syntax. //! //! ```rust @@ -65,13 +71,6 @@ //! let add = move |a: i32, b: i32| a + b + c; //! ``` //! -//! Each callable may be considered a subset of the other: -//! functions are a subset of immutable closures which are a subset of mutable closures. -//! -//! This means that, in terms of traits, you could imagine that any type that implements -//! [`IntoFunction`], also implements [`IntoClosure`] and [`IntoClosureMut`]. -//! And every type that implements [`IntoClosure`] also implements [`IntoClosureMut`]. -//! //! # Valid Signatures //! //! Many of the traits in this module have default blanket implementations over a specific set of function signatures. @@ -93,7 +92,7 @@ //! namely the [lack of variadic generics] and certain [coherence issues]. //! //! For other functions that don't conform to one of the above signatures, -//! [`DynamicFunction`] and [`DynamicClosure`] can instead be created manually. +//! [`DynamicFunction`] and [`DynamicFunctionMut`] can instead be created manually. //! //! [`PartialReflect`]: crate::PartialReflect //! [`Reflect`]: crate::Reflect @@ -101,22 +100,24 @@ //! [coherence issues]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#coherence-leak-check pub use args::{ArgError, ArgList, ArgValue}; -pub use closures::*; +pub use dynamic_function::*; +pub use dynamic_function_mut::*; pub use error::*; -pub use function::*; pub use info::*; pub use into_function::*; +pub use into_function_mut::*; pub use reflect_fn::*; pub use reflect_fn_mut::*; pub use registry::*; pub use return_type::*; pub mod args; -mod closures; +mod dynamic_function; +mod dynamic_function_mut; mod error; -mod function; mod info; mod into_function; +mod into_function_mut; pub(crate) mod macros; mod reflect_fn; mod reflect_fn_mut; diff --git a/crates/bevy_reflect/src/func/reflect_fn.rs b/crates/bevy_reflect/src/func/reflect_fn.rs index 51a028941c..1dc1d31302 100644 --- a/crates/bevy_reflect/src/func/reflect_fn.rs +++ b/crates/bevy_reflect/src/func/reflect_fn.rs @@ -13,7 +13,8 @@ use crate::{Reflect, TypePath}; /// /// This trait has a blanket implementation that covers: /// - Functions and methods defined with the `fn` keyword -/// - Closures that do not capture their environment +/// - Anonymous functions +/// - Function pointers /// - Closures that capture immutable references to their environment /// - Closures that take ownership of captured variables /// @@ -24,7 +25,7 @@ use crate::{Reflect, TypePath}; /// /// See the [module-level documentation] for more information on valid signatures. /// -/// To handle closures that capture mutable references to their environment, +/// To handle functions that capture mutable references to their environment, /// see the [`ReflectFnMut`] trait instead. /// /// Arguments are expected to implement [`FromArg`], and the return type is expected to implement [`IntoReturn`]. @@ -52,7 +53,7 @@ use crate::{Reflect, TypePath}; /// This `Marker` can be any type, provided it doesn't conflict with other implementations. /// /// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function. -/// For most functions, this will end up just being `'static`, +/// For named functions and some closures, this will end up just being `'static`, /// however, closures that borrow from their environment will have a lifetime bound to that environment. /// /// [reflection]: crate @@ -64,7 +65,7 @@ pub trait ReflectFn<'env, Marker>: ReflectFnMut<'env, Marker> { fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>; } -/// Helper macro for implementing [`ReflectFn`] on Rust closures. +/// Helper macro for implementing [`ReflectFn`] on Rust functions. /// /// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`): /// - `Fn(arg0, arg1, ..., argN) -> R` diff --git a/crates/bevy_reflect/src/func/reflect_fn_mut.rs b/crates/bevy_reflect/src/func/reflect_fn_mut.rs index ae109832e4..a6a6bd6e9f 100644 --- a/crates/bevy_reflect/src/func/reflect_fn_mut.rs +++ b/crates/bevy_reflect/src/func/reflect_fn_mut.rs @@ -9,13 +9,15 @@ use crate::{Reflect, TypePath}; /// /// This allows functions to be called dynamically through [reflection]. /// -/// This is a supertrait of [`ReflectFn`], and is used for closures that may mutate their environment. +/// This is a supertrait of [`ReflectFn`], and is used for functions that may mutate their environment, +/// such as closures that capture mutable references. /// /// # Blanket Implementation /// /// This trait has a blanket implementation that covers everything that [`ReflectFn`] does: /// - Functions and methods defined with the `fn` keyword -/// - Closures that do not capture their environment +/// - Anonymous functions +/// - Function pointers /// - Closures that capture immutable references to their environment /// - Closures that take ownership of captured variables /// @@ -57,7 +59,7 @@ use crate::{Reflect, TypePath}; /// This `Marker` can be any type, provided it doesn't conflict with other implementations. /// /// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function. -/// For most functions, this will end up just being `'static`, +/// For named functions and some closures, this will end up just being `'static`, /// however, closures that borrow from their environment will have a lifetime bound to that environment. /// /// [reflection]: crate @@ -70,7 +72,7 @@ pub trait ReflectFnMut<'env, Marker> { fn reflect_call_mut<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a>; } -/// Helper macro for implementing [`ReflectFnMut`] on Rust closures. +/// Helper macro for implementing [`ReflectFnMut`] on Rust functions. /// /// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`): /// - `FnMut(arg0, arg1, ..., argN) -> R` diff --git a/crates/bevy_reflect/src/func/registry.rs b/crates/bevy_reflect/src/func/registry.rs index f1297a925a..87f66a070d 100644 --- a/crates/bevy_reflect/src/func/registry.rs +++ b/crates/bevy_reflect/src/func/registry.rs @@ -4,40 +4,37 @@ use std::sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; use bevy_utils::HashMap; -use crate::func::{DynamicClosure, FunctionRegistrationError, IntoClosure}; +use crate::func::{DynamicFunction, FunctionRegistrationError, IntoFunction}; /// A registry of [reflected functions]. /// /// This is the function-equivalent to the [`TypeRegistry`]. /// -/// All functions and closures are stored as `'static` closures via [`DynamicClosure<'static>`]. -/// [`DynamicClosure`] is used instead of [`DynamicFunction`] as it can represent both functions and closures, -/// allowing registered functions to contain captured environment data. +/// All functions must be `'static` as they are stored as [`DynamicFunction<'static>`]. /// /// [reflected functions]: crate::func /// [`TypeRegistry`]: crate::TypeRegistry -/// [`DynamicFunction`]: crate::func::DynamicFunction #[derive(Default)] pub struct FunctionRegistry { - /// Maps function [names] to their respective [`DynamicClosures`]. + /// Maps function [names] to their respective [`DynamicFunctions`]. /// - /// [names]: DynamicClosure::name - /// [`DynamicClosures`]: DynamicClosure - functions: HashMap, DynamicClosure<'static>>, + /// [names]: DynamicFunction::name + /// [`DynamicFunctions`]: DynamicFunction + functions: HashMap, DynamicFunction<'static>>, } impl FunctionRegistry { /// Attempts to register the given function. /// - /// This function accepts both functions/closures that satisfy [`IntoClosure`] - /// and direct [`DynamicFunction`]/[`DynamicClosure`] instances. - /// The given function will internally be stored as a [`DynamicClosure<'static>`] + /// This function accepts both functions that satisfy [`IntoFunction`] + /// and direct [`DynamicFunction`] instances. + /// The given function will internally be stored as a [`DynamicFunction<'static>`] /// and mapped according to its [name]. /// /// Because the function must have a name, /// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead - /// be registered using [`register_with_name`] or converted to a [`DynamicClosure`] - /// and named using [`DynamicClosure::with_name`]. + /// be registered using [`register_with_name`] or manually converted to a [`DynamicFunction`] + /// and named using [`DynamicFunction::with_name`]. /// Failure to do so will result in an error being returned. /// /// If a registered function with the same name already exists, @@ -63,7 +60,7 @@ impl FunctionRegistry { /// Functions cannot be registered more than once. /// /// ``` - /// # use bevy_reflect::func::{DynamicFunction, FunctionRegistrationError, FunctionRegistry, IntoClosure}; + /// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry, IntoFunction}; /// fn add(a: i32, b: i32) -> i32 { /// a + b /// } @@ -76,14 +73,14 @@ impl FunctionRegistry { /// /// // Note that this simply relies on the name of the function to determine uniqueness. /// // You can rename the function to register a separate instance of it. - /// let result = registry.register(add.into_closure().with_name("add2")); + /// let result = registry.register(add.into_function().with_name("add2")); /// assert!(result.is_ok()); /// ``` /// - /// Anonymous functions and closures should be registered using [`register_with_name`] or given a name using [`DynamicClosure::with_name`]. + /// Anonymous functions and closures should be registered using [`register_with_name`] or given a name using [`DynamicFunction::with_name`]. /// /// ``` - /// # use bevy_reflect::func::{DynamicFunction, FunctionRegistrationError, FunctionRegistry, IntoClosure}; + /// # use bevy_reflect::func::{FunctionRegistrationError, FunctionRegistry, IntoFunction}; /// /// let anonymous = || -> i32 { 123 }; /// @@ -95,12 +92,11 @@ impl FunctionRegistry { /// let result = registry.register_with_name("my_crate::add", |a: i32, b: i32| a + b); /// assert!(result.is_ok()); /// - /// let result = registry.register((|a: i32, b: i32| a * b).into_closure().with_name("my_crate::mul")); + /// let result = registry.register((|a: i32, b: i32| a * b).into_function().with_name("my_crate::mul")); /// assert!(result.is_ok()); /// ``` /// - /// [`DynamicFunction`]: crate::func::DynamicFunction - /// [name]: DynamicClosure::name + /// [name]: DynamicFunction::name /// [`register_with_name`]: Self::register_with_name /// [`overwrite_registration`]: Self::overwrite_registration pub fn register( @@ -108,15 +104,15 @@ impl FunctionRegistry { function: F, ) -> Result<&mut Self, FunctionRegistrationError> where - F: IntoClosure<'static, Marker> + 'static, + F: IntoFunction<'static, Marker> + 'static, { - let function = function.into_closure(); + let function = function.into_function(); let name = function .name() .ok_or(FunctionRegistrationError::MissingName)? .clone(); self.functions - .try_insert(name, function.into_closure()) + .try_insert(name, function.into_function()) .map_err(|err| FunctionRegistrationError::DuplicateName(err.entry.key().clone()))?; Ok(self) @@ -124,9 +120,9 @@ impl FunctionRegistry { /// Attempts to register the given function with the given name. /// - /// This function accepts both functions/closures that satisfy [`IntoClosure`] - /// and direct [`DynamicFunction`]/[`DynamicClosure`] instances. - /// The given function will internally be stored as a [`DynamicClosure<'static>`] + /// This function accepts both functions that satisfy [`IntoFunction`] + /// and direct [`DynamicFunction`] instances. + /// The given function will internally be stored as a [`DynamicFunction<'static>`] /// with its [name] set to the given name. /// /// For named functions (e.g. `fn add(a: i32, b: i32) -> i32 { a + b }`) where a custom name is not needed, @@ -145,9 +141,10 @@ impl FunctionRegistry { /// you could use the name, `"my_crate::add"`. /// /// Another approach could be to use the [type name] of the function, - /// however, it should be noted that anonymous functions do _not_ have unique type names. + /// however, it should be noted that anonymous functions and closures + ///are not guaranteed to have unique type names. /// - /// This method is a convenience around calling [`IntoClosure::into_closure`] and [`DynamicClosure::with_name`] + /// This method is a convenience around calling [`IntoFunction::into_function`] and [`DynamicFunction::with_name`] /// on the function and inserting it into the registry using the [`register`] method. /// /// # Examples @@ -193,8 +190,7 @@ impl FunctionRegistry { /// registry.register_with_name("my_function", two).unwrap(); /// ``` /// - /// [`DynamicFunction`]: crate::func::DynamicFunction - /// [name]: DynamicClosure::name + /// [name]: DynamicFunction::name /// [`register`]: Self::register /// [`overwrite_registration_with_name`]: Self::overwrite_registration_with_name /// [type name]: std::any::type_name @@ -204,23 +200,23 @@ impl FunctionRegistry { function: F, ) -> Result<&mut Self, FunctionRegistrationError> where - F: IntoClosure<'static, Marker> + 'static, + F: IntoFunction<'static, Marker> + 'static, { - let function = function.into_closure().with_name(name); + let function = function.into_function().with_name(name); self.register(function) } /// Registers the given function, overwriting any existing registration. /// - /// This function accepts both functions/closures that satisfy [`IntoClosure`] - /// and direct [`DynamicFunction`]/[`DynamicClosure`] instances. - /// The given function will internally be stored as a [`DynamicClosure<'static>`] + /// This function accepts both functions that satisfy [`IntoFunction`] + /// and direct [`DynamicFunction`] instances. + /// The given function will internally be stored as a [`DynamicFunction<'static>`] /// and mapped according to its [name]. /// /// Because the function must have a name, /// anonymous functions (e.g. `|a: i32, b: i32| { a + b }`) and closures must instead - /// be registered using [`overwrite_registration_with_name`] or converted to a [`DynamicClosure`] - /// and named using [`DynamicClosure::with_name`]. + /// be registered using [`overwrite_registration_with_name`] or manually converted to a [`DynamicFunction`] + /// and named using [`DynamicFunction::with_name`]. /// Failure to do so will result in an error being returned. /// /// To avoid overwriting existing registrations, @@ -228,18 +224,17 @@ impl FunctionRegistry { /// /// Returns the previous function with the same name, if any. /// - /// [`DynamicFunction`]: crate::func::DynamicFunction - /// [name]: DynamicClosure::name + /// [name]: DynamicFunction::name /// [`overwrite_registration_with_name`]: Self::overwrite_registration_with_name /// [`register`]: Self::register pub fn overwrite_registration( &mut self, function: F, - ) -> Result>, FunctionRegistrationError> + ) -> Result>, FunctionRegistrationError> where - F: IntoClosure<'static, Marker> + 'static, + F: IntoFunction<'static, Marker> + 'static, { - let function = function.into_closure(); + let function = function.into_function(); let name = function .name() .ok_or(FunctionRegistrationError::MissingName)? @@ -250,33 +245,32 @@ impl FunctionRegistry { /// Registers the given function, overwriting any existing registration. /// - /// This function accepts both functions/closures that satisfy [`IntoClosure`] - /// and direct [`DynamicFunction`]/[`DynamicClosure`] instances. - /// The given function will internally be stored as a [`DynamicClosure<'static>`] + /// This function accepts both functions that satisfy [`IntoFunction`] + /// and direct [`DynamicFunction`] instances. + /// The given function will internally be stored as a [`DynamicFunction<'static>`] /// with its [name] set to the given name. /// /// Functions are mapped according to their name. /// To avoid overwriting existing registrations, /// it's recommended to use the [`register_with_name`] method instead. /// - /// This method is a convenience around calling [`IntoClosure::into_closure`] and [`DynamicClosure::with_name`] + /// This method is a convenience around calling [`IntoFunction::into_function`] and [`DynamicFunction::with_name`] /// on the function and inserting it into the registry using the [`overwrite_registration`] method. /// /// Returns the previous function with the same name, if any. /// - /// [`DynamicFunction`]: crate::func::DynamicFunction - /// [name]: DynamicClosure::name + /// [name]: DynamicFunction::name /// [`register_with_name`]: Self::register_with_name /// [`overwrite_registration`]: Self::overwrite_registration pub fn overwrite_registration_with_name( &mut self, name: impl Into>, function: F, - ) -> Option> + ) -> Option> where - F: IntoClosure<'static, Marker> + 'static, + F: IntoFunction<'static, Marker> + 'static, { - let function = function.into_closure().with_name(name); + let function = function.into_function().with_name(name); match self.overwrite_registration(function) { Ok(existing) => existing, Err(FunctionRegistrationError::MissingName) => { @@ -288,31 +282,31 @@ impl FunctionRegistry { } } - /// Get a reference to a registered function or closure by [name]. + /// Get a reference to a registered function by [name]. /// - /// [name]: DynamicClosure::name - pub fn get(&self, name: &str) -> Option<&DynamicClosure<'static>> { + /// [name]: DynamicFunction::name + pub fn get(&self, name: &str) -> Option<&DynamicFunction<'static>> { self.functions.get(name) } - /// Returns `true` if a function or closure with the given [name] is registered. + /// Returns `true` if a function with the given [name] is registered. /// - /// [name]: DynamicClosure::name + /// [name]: DynamicFunction::name pub fn contains(&self, name: &str) -> bool { self.functions.contains_key(name) } - /// Returns an iterator over all registered functions/closures. - pub fn iter(&self) -> impl ExactSizeIterator> { + /// Returns an iterator over all registered functions. + pub fn iter(&self) -> impl ExactSizeIterator> { self.functions.values() } - /// Returns the number of registered functions/closures. + /// Returns the number of registered functions. pub fn len(&self) -> usize { self.functions.len() } - /// Returns `true` if no functions or closures are registered. + /// Returns `true` if no functions are registered. pub fn is_empty(&self) -> bool { self.functions.is_empty() } @@ -407,7 +401,7 @@ mod tests { let value = 123; let foo = move || -> i32 { value }; - let function = foo.into_closure().with_name("custom_name"); + let function = foo.into_function().with_name("custom_name"); let mut registry = FunctionRegistry::default(); registry.register(function).unwrap(); @@ -494,6 +488,6 @@ mod tests { registry.register_with_name("foo", foo).unwrap(); let debug = format!("{:?}", registry); - assert_eq!(debug, "{DynamicClosure(fn foo() -> i32)}"); + assert_eq!(debug, "{DynamicFunction(fn foo() -> i32)}"); } } diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs index 02f76c974e..9ad44018d9 100644 --- a/crates/bevy_reflect/src/func/return_type.rs +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -1,9 +1,9 @@ use crate::PartialReflect; -/// The return type of a [`DynamicFunction`] or [`DynamicClosure`]. +/// The return type of a [`DynamicFunction`] or [`DynamicFunctionMut`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction -/// [`DynamicClosure`]: crate::func::DynamicClosure +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug)] pub enum Return<'a> { /// The function returns nothing (i.e. it returns `()`). @@ -62,14 +62,15 @@ impl<'a> Return<'a> { /// A trait for types that can be converted into a [`Return`] value. /// /// This trait exists so that types can be automatically converted into a [`Return`] -/// by [`IntoFunction`]. +/// by [`ReflectFn`] and [`ReflectFnMut`]. /// /// This trait is used instead of a blanket [`Into`] implementation due to coherence issues: /// we can't implement `Into` for both `T` and `&T`/`&mut T`. /// /// This trait is automatically implemented when using the `Reflect` [derive macro]. /// -/// [`IntoFunction`]: crate::func::IntoFunction +/// [`ReflectFn`]: crate::func::ReflectFn +/// [`ReflectFnMut`]: crate::func::ReflectFnMut /// [derive macro]: derive@crate::Reflect pub trait IntoReturn { /// Converts [`Self`] into a [`Return`] value. diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 87e7704835..fca875b70b 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -574,7 +574,7 @@ pub mod prelude { }; #[cfg(feature = "functions")] - pub use crate::func::IntoFunction; + pub use crate::func::{IntoFunction, IntoFunctionMut}; } pub use array::*; diff --git a/examples/reflection/function_reflection.rs b/examples/reflection/function_reflection.rs index 25fe09e639..b004d2020f 100644 --- a/examples/reflection/function_reflection.rs +++ b/examples/reflection/function_reflection.rs @@ -7,8 +7,8 @@ //! processing deserialized reflection data, or even just storing type-erased versions of your functions. use bevy::reflect::func::{ - ArgList, DynamicClosure, DynamicClosureMut, DynamicFunction, FunctionError, FunctionInfo, - IntoClosure, IntoClosureMut, IntoFunction, Return, + ArgList, DynamicFunction, DynamicFunctionMut, FunctionError, FunctionInfo, IntoFunction, + IntoFunctionMut, Return, }; use bevy::reflect::{PartialReflect, Reflect}; @@ -37,7 +37,7 @@ fn main() { // Luckily, Bevy's reflection crate comes with a set of tools for doing just that! // We do this by first converting our function into the reflection-based `DynamicFunction` type // using the `IntoFunction` trait. - let function: DynamicFunction = dbg!(add.into_function()); + let function: DynamicFunction<'static> = dbg!(add.into_function()); // This time, you'll notice that `DynamicFunction` doesn't take any information about the function's arguments or return value. // This is because `DynamicFunction` checks the types of the arguments and return value at runtime. @@ -56,27 +56,28 @@ fn main() { assert_eq!(value.try_take::().unwrap(), 4); // The same can also be done for closures that capture references to their environment. - // Closures that capture their environment immutably can be converted into a `DynamicClosure` - // using the `IntoClosure` trait. + // Closures that capture their environment immutably can be converted into a `DynamicFunction` + // using the `IntoFunction` trait. let minimum = 5; let clamp = |value: i32| value.max(minimum); - let function: DynamicClosure = dbg!(clamp.into_closure()); + let function: DynamicFunction = dbg!(clamp.into_function()); let args = dbg!(ArgList::new().push_owned(2_i32)); let return_value = dbg!(function.call(args).unwrap()); let value: Box = return_value.unwrap_owned(); assert_eq!(value.try_take::().unwrap(), 5); // We can also handle closures that capture their environment mutably - // using the `IntoClosureMut` trait. + // using the `IntoFunctionMut` trait. let mut count = 0; let increment = |amount: i32| count += amount; - let closure: DynamicClosureMut = dbg!(increment.into_closure_mut()); + let closure: DynamicFunctionMut = dbg!(increment.into_function_mut()); let args = dbg!(ArgList::new().push_owned(5_i32)); - // Because `DynamicClosureMut` mutably borrows `total`, + + // Because `DynamicFunctionMut` mutably borrows `total`, // it will need to be dropped before `total` can be accessed again. - // This can be done manually with `drop(closure)` or by using the `DynamicClosureMut::call_once` method. + // This can be done manually with `drop(closure)` or by using the `DynamicFunctionMut::call_once` method. dbg!(closure.call_once(args).unwrap()); assert_eq!(count, 5);