mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
af865e76a3
# 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. |
||
---|---|---|
.. | ||
custom_attributes.rs | ||
dynamic_types.rs | ||
function_reflection.rs | ||
generic_reflection.rs | ||
reflection.rs | ||
reflection_types.rs | ||
type_data.rs |