I'm working on adding dynamic, reflection-based function types for the Bevy game engine. This is meant to allow something like the following:
let mut add: Func = Foo::add.into_func();
let args: ArgList = ArgList::default().push_mut(&mut foo).push_owned(50);
add.call(args).unwrap();
println!("{:?}", foo);
This almost works. The issue is that the lifetimes require me to add a drop(add) just before the println!, since foo is "already borrowed as mutable" and "mutable borrow might be used here, when add is dropped and runs the destructor for type Func<'_>".
Of course, I want to be able to call add multiple times if necessary. And even if not that, I don't want to have to manually drop/scope add just so I can resume using foo.
Here are the relevant types and traits (simplified a bit):
enum Arg<'a> {
Owned(Box<dyn Reflect>),
Ref(&'a dyn Reflect),
Mut(&'a mut dyn Reflect),
}
/// Used to convert `Arg<'a>` to one of:
/// 1. `T`
/// 2. `&'a T`
/// 3. `&'a mut T`
/// This allows us to avoid manually hundreds of impls with different ownership combinations
trait FromArg<'a>: Sized {
fn from_arg(arg: Arg<'a>, info: &ArgInfo) -> Result<Self, ArgError>;
}
struct Func<'a> {
func: Box<dyn FnMut(ArgList<'a>, &[ArgInfo]) -> FuncResult>,
}
trait IntoFunc<'a, T> {
fn into_func(self) -> Func<'a>;
}
And this is the impl that manages to compile:
impl<'a, T0: FromArg<'a>, R: Reflect, F: FnMut(T0) -> R> IntoFunc<'a, fn(T0) -> R> for F {
fn into_func(mut self) -> Func<'a> {
Func {
func: Box::new(move |mut args, info| {
let output = (self)(args.take_1(info)?);
to_func_return(output)
}),
}
}
}
I think the problem is because ArgList shares the lifetime 'a with Func. However, other configurations don't seem to work.


