bevy/crates/bevy_reflect/src/func/reflect_fn.rs
Gino Valente 2b4180ca8f
bevy_reflect: Function reflection terminology refactor (#14813)
# Objective

One of the changes in #14704 made `DynamicFunction` effectively the same
as `DynamicClosure<'static>`. This change meant that the de facto
function type would likely be `DynamicClosure<'static>` instead of the
intended `DynamicFunction`, since the former is much more flexible.

We _could_ explore ways of making `DynamicFunction` implement `Copy`
using some unsafe code, but it likely wouldn't be worth it. And users
would likely still reach for the convenience of
`DynamicClosure<'static>` over the copy-ability of `DynamicFunction`.

The goal of this PR is to fix this confusion between the two types.

## Solution

Firstly, the `DynamicFunction` type was removed. Again, it was no
different than `DynamicClosure<'static>` so it wasn't a huge deal to
remove.

Secondly, `DynamicClosure<'env>` and `DynamicClosureMut<'env>` were
renamed to `DynamicFunction<'env>` and `DynamicFunctionMut<'env>`,
respectively.

Yes, we still ultimately kept the naming of `DynamicFunction`, but
changed its behavior to that of `DynamicClosure<'env>`. We need a term
to refer to both functions and closures, and "function" was the best
option.


[Originally](https://discord.com/channels/691052431525675048/1002362493634629796/1274091992162242710),
I was going to go with "callable" as the replacement term to encompass
both functions and closures (e.g. `DynamciCallable<'env>`). However, it
was
[suggested](https://discord.com/channels/691052431525675048/1002362493634629796/1274653581777047625)
by @SkiFire13 that the simpler "function" term could be used instead.

While "callable" is perhaps the better umbrella term—being truly
ambiguous over functions and closures— "function" is more familiar, used
more often, easier to discover, and is subjectively just
"better-sounding".

## Testing

Most changes are purely swapping type names or updating documentation,
but you can verify everything still works by running the following
command:

```
cargo test --package bevy_reflect
```
2024-08-19 21:52:36 +00:00

197 lines
8.4 KiB
Rust

use bevy_utils::all_tuples;
use crate::func::args::FromArg;
use crate::func::macros::count_tokens;
use crate::func::{ArgList, FunctionError, FunctionResult, IntoReturn, ReflectFnMut};
use crate::{Reflect, TypePath};
/// A reflection-based version of the [`Fn`] trait.
///
/// This allows functions to be called dynamically through [reflection].
///
/// # Blanket Implementation
///
/// This trait has a blanket implementation that covers:
/// - Functions and methods defined with the `fn` keyword
/// - Anonymous functions
/// - Function pointers
/// - Closures that capture immutable references to their environment
/// - Closures that take ownership of captured variables
///
/// For each of the above cases, the function signature may only have up to 15 arguments,
/// not including an optional receiver argument (often `&self` or `&mut self`).
/// This optional receiver argument may be either a mutable or immutable reference to a type.
/// If the return type is also a reference, its lifetime will be bound to the lifetime of this receiver.
///
/// See the [module-level documentation] for more information on valid signatures.
///
/// 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`].
/// Both of these traits are automatically implemented when using the `Reflect` [derive macro].
///
/// # Example
///
/// ```
/// # use bevy_reflect::func::{ArgList, FunctionInfo, ReflectFn};
/// #
/// fn add(a: i32, b: i32) -> i32 {
/// a + b
/// }
///
/// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32);
///
/// let value = add.reflect_call(args).unwrap().unwrap_owned();
/// assert_eq!(value.try_take::<i32>().unwrap(), 100);
/// ```
///
/// # 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.
///
/// [reflection]: crate
/// [module-level documentation]: crate::func
/// [derive macro]: derive@crate::Reflect
/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html
pub trait ReflectFn<'env, Marker>: ReflectFnMut<'env, Marker> {
/// Call the function with the given arguments and return the result.
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>;
}
/// 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`
/// - `Fn(&Receiver, arg0, arg1, ..., argN) -> &R`
/// - `Fn(&mut Receiver, arg0, arg1, ..., argN) -> &mut R`
/// - `Fn(&mut Receiver, arg0, arg1, ..., argN) -> &R`
macro_rules! impl_reflect_fn {
($(($Arg:ident, $arg:ident)),*) => {
// === (...) -> ReturnType === //
impl<'env, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn($($Arg),*) -> [ReturnType]> for Function
where
$($Arg: FromArg,)*
// This clause allows us to convert `ReturnType` into `Return`
ReturnType: IntoReturn + Reflect,
Function: Fn($($Arg),*) -> ReturnType + 'env,
// This clause essentially asserts that `Arg::This` is the same type as `Arg`
Function: for<'a> Fn($($Arg::This<'a>),*) -> ReturnType + 'env,
{
#[allow(unused_mut)]
fn reflect_call<'a>(&self, mut args: ArgList<'a>) -> FunctionResult<'a> {
const COUNT: usize = count_tokens!($($Arg)*);
if args.len() != COUNT {
return Err(FunctionError::ArgCountMismatch {
expected: COUNT,
received: args.len(),
});
}
// Extract all arguments (in order)
$(let $arg = args.take::<$Arg>()?;)*
Ok((self)($($arg,)*).into_return())
}
}
// === (&self, ...) -> &ReturnType === //
impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn(&Receiver, $($Arg),*) -> &ReturnType> for Function
where
Receiver: Reflect + TypePath,
$($Arg: FromArg,)*
ReturnType: Reflect,
// This clause allows us to convert `&ReturnType` into `Return`
for<'a> &'a ReturnType: IntoReturn,
Function: for<'a> Fn(&'a Receiver, $($Arg),*) -> &'a ReturnType + 'env,
// This clause essentially asserts that `Arg::This` is the same type as `Arg`
Function: for<'a> Fn(&'a Receiver, $($Arg::This<'a>),*) -> &'a ReturnType + 'env,
{
fn reflect_call<'a>(&self, mut args: ArgList<'a>) -> FunctionResult<'a> {
const COUNT: usize = count_tokens!(Receiver $($Arg)*);
if args.len() != COUNT {
return Err(FunctionError::ArgCountMismatch {
expected: COUNT,
received: args.len(),
});
}
// Extract all arguments (in order)
let receiver = args.take_ref::<Receiver>()?;
$(let $arg = args.take::<$Arg>()?;)*
Ok((self)(receiver, $($arg,)*).into_return())
}
}
// === (&mut self, ...) -> &mut ReturnType === //
impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn(&mut Receiver, $($Arg),*) -> &mut ReturnType> for Function
where
Receiver: Reflect + TypePath,
$($Arg: FromArg,)*
ReturnType: Reflect,
// This clause allows us to convert `&mut ReturnType` into `Return`
for<'a> &'a mut ReturnType: IntoReturn,
Function: for<'a> Fn(&'a mut Receiver, $($Arg),*) -> &'a mut ReturnType + 'env,
// This clause essentially asserts that `Arg::This` is the same type as `Arg`
Function: for<'a> Fn(&'a mut Receiver, $($Arg::This<'a>),*) -> &'a mut ReturnType + 'env,
{
fn reflect_call<'a>(&self, mut args: ArgList<'a>) -> FunctionResult<'a> {
const COUNT: usize = count_tokens!(Receiver $($Arg)*);
if args.len() != COUNT {
return Err(FunctionError::ArgCountMismatch {
expected: COUNT,
received: args.len(),
});
}
// Extract all arguments (in order)
let receiver = args.take_mut::<Receiver>()?;
$(let $arg = args.take::<$Arg>()?;)*
Ok((self)(receiver, $($arg,)*).into_return())
}
}
// === (&mut self, ...) -> &ReturnType === //
impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn(&mut Receiver, $($Arg),*) -> &ReturnType> for Function
where
Receiver: Reflect + TypePath,
$($Arg: FromArg,)*
ReturnType: Reflect,
// This clause allows us to convert `&ReturnType` into `Return`
for<'a> &'a ReturnType: IntoReturn,
Function: for<'a> Fn(&'a mut Receiver, $($Arg),*) -> &'a ReturnType + 'env,
// This clause essentially asserts that `Arg::This` is the same type as `Arg`
Function: for<'a> Fn(&'a mut Receiver, $($Arg::This<'a>),*) -> &'a ReturnType + 'env,
{
fn reflect_call<'a>(&self, mut args: ArgList<'a>) -> FunctionResult<'a> {
const COUNT: usize = count_tokens!(Receiver $($Arg)*);
if args.len() != COUNT {
return Err(FunctionError::ArgCountMismatch {
expected: COUNT,
received: args.len(),
});
}
// Extract all arguments (in order)
let receiver = args.take_mut::<Receiver>()?;
$(let $arg = args.take::<$Arg>()?;)*
Ok((self)(receiver, $($arg,)*).into_return())
}
}
};
}
all_tuples!(impl_reflect_fn, 0, 15, Arg, arg);