I'm trying to create a generic task runner for a data source that allows for functions that accept generic fetched parameters.
However I run into 2 problems - type inference for the IntoSystem trait and lifetime issues if any parameters use lifetimes.
Any help is much appreciated.
Playground - whole example here
Here is what i would like to work...
1 - Declare some work functions
/// A work function
fn sys_source(source: &Source) {
println!("source system - {}", source.age.borrow());
}
/// A fetch work function
fn sys_u32(age: u32) {
println!("age system - {}", age);
}
/// A fetch work function using a reference
fn sys_ref(age: Ref<u32>) {
println!("ref system - {}", *age);
}
2 - Convert them to systems using the trait IntoSystem
let sys_a: System = sys_source.into_system(); // Works
let sys_b: System = sys_u32.into_system(); // error[E0282]: type annotations needed
let sys_c: System = sys_ref.into_system(); // error[E0282]: type annotations needed
3 - Run them
sys_a.run(&source);
sys_b.run(&source);
sys_c.run(&source);
So there are 2 questions here:
Question 1 - What has to change to make the simpler A.into_system() format work?
let sys_b: System = sys_u32.into_system(); // error[E0282]: type annotations needed
let sys_b: System = <fn(u32) as IntoSystem<(u32,)>>::into_system(sys_u32); // Works
Question 2 - Even with that change, the lifetimes of references don't work. What has to change to make the lifetime limited fetches work?
let sys_c: System = sys_ref.into_system(); // error[E0282]: type annotations needed
let sys_c: System = <fn(Ref<u32>) as IntoSystem<(Ref<u32>,)>>::into_system(sys_ref); // error: implementation of `Fetch` is not general enough
// = note: `Fetch<'0>` would have to be implemented for the type `Ref<'_, u32>`, for any lifetime `'0`...
// = note: ...but `Fetch<'1>` is actually implemented for the type `Ref<'1, u32>`, for some specific lifetime `'1`
Here is my System and IntoSystem implementations:
/// A function that operates on a Source
struct System {
func: Box<dyn Fn(&Source) -> ()>,
}
impl System {
/// Construct a new System with the given work function
fn new(func: Box<dyn Fn(&Source) -> ()> ) -> Self {
System { func }
}
/// Run the system's work function
fn run(&self, source: &Source) {
(self.func)(source);
}
}
/// Converts a type into a system
trait IntoSystem<A> {
fn into_system(self) -> System;
}
/// Make a system for a Source level func
impl<F> IntoSystem<&Source> for F
where
F: Fn(&Source) -> () + 'static
{
fn into_system(self) -> System {
System::new(Box::new(move |source| {
(self)(source);
}))
}
}
/// Make a system that takes a single fetch parameter
impl<F,A> IntoSystem<(A,)> for F
where
for<'a> A: Fetch<'a>,
for<'a> F: Fn(<A as Fetch<'a>>::Output) -> () + 'static
{
fn into_system(self) -> System {
System::new(Box::new(move |source| {
let data = A::fetch(source);
(self)(data);
}))
}
}
/// Make a system that takes 2 fetch parameters
impl<F,A,B> IntoSystem<(A,B)> for F
where
for<'a> A: Fetch<'a> + 'a,
for<'a> B: Fetch<'a> + 'a,
for<'a> F: Fn(<A as Fetch<'a>>::Output, <B as Fetch<'a>>::Output) -> () + 'static
{
fn into_system(self) -> System {
System::new(Box::new(move |source| {
let data = <(A,B)>::fetch(source);
(self)(data.0, data.1);
}))
}
}
And here is the Fetch stuff...
/// Trait to allow fetching arbitrary parts of the Source
trait Fetch<'a> {
type Output;
fn fetch(source: &'a Source) -> Self::Output;
}
/// Fetch the age as a u32
impl<'a> Fetch<'a> for u32 {
type Output = u32;
fn fetch(source: &'a Source) -> Self::Output {
*source.age.borrow()
}
}
/// Fetch the age as a Ref<u32>
impl<'a> Fetch<'a> for Ref<'a, u32> {
type Output = Ref<'a, u32>;
fn fetch(source: &'a Source) -> Self::Output {
source.age.borrow()
}
}
/// Fetch any item as a single tuple
impl<'a, A> Fetch<'a> for (A,)
where A: Fetch<'a> + 'a
{
type Output = (<A as Fetch<'a>>::Output,);
fn fetch(source: &'a Source) -> Self::Output {
(A::fetch(source),)
}
}
/// Fetch any item as a pair tuple
impl<'a, A, B> Fetch<'a> for (A,B)
where
A: Fetch<'a> + 'a,
B: Fetch<'a> + 'a
{
type Output = (<A as Fetch<'a>>::Output,<B as Fetch<'a>>::Output);
fn fetch(source: &'a Source) -> Self::Output {
(A::fetch(source),B::fetch(source))
}
}