bevy/crates
Gino Valente af865e76a3
bevy_reflect: Improve DynamicFunction ergonomics (#14201)
# Objective

Many functions can be converted to `DynamicFunction` using
`IntoFunction`. Unfortunately, we are limited by Rust itself and the
implementations are far from exhaustive. For example, we can't convert
functions with more than 16 arguments. Additionally, we can't handle
returns with lifetimes not tied to the lifetime of the first argument.

In such cases, users will have to create their `DynamicFunction`
manually.

Let's take the following function:

```rust
fn get(index: usize, list: &Vec<String>) -> &String {
    &list[index]
}
```

This function cannot be converted to a `DynamicFunction` via
`IntoFunction` due to the lifetime of the return value being tied to the
second argument. Therefore, we need to construct the `DynamicFunction`
manually:

```rust
DynamicFunction::new(
    |mut args, info| {
        let list = args
            .pop()
            .unwrap()
            .take_ref::<Vec<String>>(&info.args()[1])?;
        let index = args.pop().unwrap().take_owned::<usize>(&info.args()[0])?;
        Ok(Return::Ref(get(index, list)))
    },
    FunctionInfo::new()
        .with_name("get")
        .with_args(vec![
            ArgInfo:🆕:<usize>(0).with_name("index"),
            ArgInfo:🆕:<&Vec<String>>(1).with_name("list"),
        ])
        .with_return_info(ReturnInfo:🆕:<&String>()),
);
```

While still a small and straightforward snippet, there's a decent amount
going on here. There's a lot of room for improvements when it comes to
ergonomics and readability.

The goal of this PR is to address those issues.

## Solution

Improve the ergonomics and readability of manually created
`DynamicFunction`s.

Some of the major changes:
1. Removed the need for `&ArgInfo` when reifying arguments (i.e. the
`&info.args()[1]` calls)
2. Added additional `pop` methods on `ArgList` to handle both popping
and casting
3. Added `take` methods on `ArgList` for taking the arguments out in
order
4. Removed the need for `&FunctionInfo` in the internal closure (Change
1 made it no longer necessary)
5. Added methods to automatically handle generating `ArgInfo` and
`ReturnInfo`

With all these changes in place, we get something a lot nicer to both
write and look at:

```rust
DynamicFunction::new(
    |mut args| {
        let index = args.take::<usize>()?;
        let list = args.take::<&Vec<String>>()?;
        Ok(Return::Ref(get(index, list)))
    },
    FunctionInfo::new()
        .with_name("get")
        .with_arg::<usize>("index")
        .with_arg::<&Vec<String>>("list")
        .with_return::<&String>(),
);
```

Alternatively, to rely on type inference for taking arguments, you could
do:

```rust
DynamicFunction::new(
    |mut args| {
        let index = args.take_owned()?;
        let list = args.take_ref()?;
        Ok(Return::Ref(get(index, list)))
    },
    FunctionInfo::new()
        .with_name("get")
        .with_arg::<usize>("index")
        .with_arg::<&Vec<String>>("list")
        .with_return::<&String>(),
);
```

## Testing

You can test locally by running:

```
cargo test --package bevy_reflect
```

---

## Changelog

- Removed `&ArgInfo` argument from `FromArg::from_arg` trait method
- Removed `&ArgInfo` argument from `Arg::take_***` methods
- Added `ArgValue`
- `Arg` is now a struct containing an `ArgValue` and an argument `index`
- `Arg::take_***` methods now require `T` is also `TypePath`
- Added `Arg::new`, `Arg::index`, `Arg::value`, `Arg::take_value`, and
`Arg::take` methods
- Replaced `ArgId` in `ArgError` with just the argument `index`
- Added `ArgError::EmptyArgList`
- Renamed `ArgList::push` to `ArgList::push_arg`
- Added `ArgList::pop_arg`, `ArgList::pop_owned`, `ArgList::pop_ref`,
and `ArgList::pop_mut`
- Added `ArgList::take_arg`, `ArgList::take_owned`, `ArgList::take_ref`,
`ArgList::take_mut`, and `ArgList::take`
- `ArgList::pop` is now generic
- Renamed `FunctionError::InvalidArgCount` to
`FunctionError::ArgCountMismatch`
- The closure given to `DynamicFunction::new` no longer has a
`&FunctionInfo` argument
- Added `FunctionInfo::with_arg`
- Added `FunctionInfo::with_return`

## Internal Migration Guide

> [!important]
> Function reflection was introduced as part of the 0.15 dev cycle. This
migration guide was written for developers relying on `main` during this
cycle, and is not a breaking change coming from 0.14.

* The `FromArg::from_arg` trait method and the `Arg::take_***` methods
no longer take a `&ArgInfo` argument.
* What used to be `Arg` is now `ArgValue`. `Arg` is now a struct which
contains an `ArgValue`.
* `Arg::take_***` methods now require `T` is also `TypePath`
* Instances of `id: ArgId` in `ArgError` have been replaced with `index:
usize`
* `ArgList::push` is now `ArgList::push_arg`. It also takes the new
`ArgValue` type.
* `ArgList::pop` has become `ArgList::pop_arg` and now returns
`ArgValue`. `Arg::pop` now takes a generic type and downcasts to that
type. It's recommended to use `ArgList::take` and friends instead since
they allow removing the arguments from the list in the order they were
pushed (rather than reverse order).
* `FunctionError::InvalidArgCount` is now
`FunctionError::ArgCountMismatch`
* The closure given to `DynamicFunction::new` no longer has a
`&FunctionInfo` argument. This argument can be removed.
2024-07-16 13:01:52 +00:00
..
bevy_a11y plugin_group! macro (adopted) (#14339) 2024-07-16 01:14:33 +00:00
bevy_animation Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_app plugin_group! macro (adopted) (#14339) 2024-07-16 01:14:33 +00:00
bevy_asset add debug logging to ascertain the base path the asset server is using (#13820) 2024-07-15 14:00:43 +00:00
bevy_audio Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_color Fix intra-doc links and make CI test them (#14076) 2024-07-11 13:08:31 +00:00
bevy_core Rename bevy_core::name::DebugName to bevy_core::name::NameOrEntity (#14211) 2024-07-15 15:21:41 +00:00
bevy_core_pipeline Fix error/typo in SMAA shader (#14338) 2024-07-15 23:40:39 +00:00
bevy_derive Specify test group names in github summary for compile fail tests (#14330) 2024-07-15 16:13:03 +00:00
bevy_dev_tools plugin_group! macro (adopted) (#14339) 2024-07-16 01:14:33 +00:00
bevy_diagnostic Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_dylib Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_dynamic_plugin Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_ecs Add insert_by_id and try_insert_by_id to EntityCommands (#14283) 2024-07-15 23:29:13 +00:00
bevy_encase_derive Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_gilrs Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_gizmos plugin_group! macro (adopted) (#14339) 2024-07-16 01:14:33 +00:00
bevy_gltf Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_hierarchy Minimal Bubbling Observers (#13991) 2024-07-15 13:39:41 +00:00
bevy_input Expose Winit's KeyEvent::repeat in KeyboardInput (#14161) 2024-07-15 14:52:33 +00:00
bevy_internal plugin_group! macro (adopted) (#14339) 2024-07-16 01:14:33 +00:00
bevy_log Fix intra-doc links and make CI test them (#14076) 2024-07-11 13:08:31 +00:00
bevy_macro_utils Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_math Added new method to Cone 3D primitive (#14325) 2024-07-16 12:59:26 +00:00
bevy_mikktspace Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_pbr Allow volumetric fog to be localized to specific, optionally voxelized, regions. (#14099) 2024-07-16 03:14:12 +00:00
bevy_picking Fix intra-doc links and make CI test them (#14076) 2024-07-11 13:08:31 +00:00
bevy_ptr Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_reflect bevy_reflect: Improve DynamicFunction ergonomics (#14201) 2024-07-16 13:01:52 +00:00
bevy_render Clearer spatial bundle pub const docs (#14293) 2024-07-15 16:03:09 +00:00
bevy_scene Align Scene::write_to_world_with to match DynamicScene::write_to_world_with (#13855) 2024-07-15 14:04:09 +00:00
bevy_sprite Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_state plugin_group! macro (adopted) (#14339) 2024-07-16 01:14:33 +00:00
bevy_tasks Make Tasks functional on WASM (#13889) 2024-07-16 01:15:03 +00:00
bevy_text Fix intra-doc links and make CI test them (#14076) 2024-07-11 13:08:31 +00:00
bevy_time Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_transform Clarify GlobalTransform::transform_point (#14292) 2024-07-15 15:59:29 +00:00
bevy_ui Clean up UiSystem system sets (#14228) 2024-07-15 15:27:38 +00:00
bevy_utils Bump Version after Release (#14219) 2024-07-08 12:54:08 +00:00
bevy_window Remove unused default feature from bevy_window (#14313) 2024-07-15 16:49:00 +00:00
bevy_winit Remove need for EventLoopProxy to be NonSend (#14198) 2024-07-16 06:59:01 +00:00