Workload creation
There are a few trickeries going on with workload's creation.
In this chapter we'll look under the hood to understand how shipyard accept:
Workload::builder("Add & Check")
.with_system(add);
IntoBorrow
Let's start with Workload::with_system.
It should accept any system, a system being a function with 0 to 10 views as arguments and returning anything.
Since it has to accept multiple types we have to make a trait, IntoWorkloadSystem.
Ideally this trait would be implemented like this:
trait Borrow {
type View<'v>;
fn borrow<'a>(world: &'a World) -> Result<Self::View<'a>, error::GetStorage>;
}
impl<$($view: Borrow + BorrowInfo,)+ R, Sys> IntoWorkloadSystem<($($view,)+), R> for Sys
where
Sys:
Fn($($view),+) -> R
+ 'static
+ Send
+ Sync {
But GAT are not stable so we can't have View<'v> as an associated type.
Today we have to write:
trait Borrow<'v> {
type View;
fn borrow(world: &'v World) -> Result<Self::View, error::GetStorage>;
}
Then IntoWorkloadSystem becomes:
impl<$($view: for<'v> Borrow<'v> + BorrowInfo,)+ R, Sys> IntoWorkloadSystem<($($view,)+), R> for Sys
where
Sys:
Fn($($view),+) -> R
+ 'static
+ Send
+ Sync {
But the compiler isn't happy.
At the end of the day, views don't implement Borrow for all lifetimes. Views only implement Borrow for their lifetime.
For example View<'a, T> will only implement Borrow<'a>, if you try Borrow<'b> it shouldn't work.
And you can see it with (), the unit type actually implements Borrow for all lifetimes and the compiler will accept functions that take a unit as argument.
So instead we don't make a single Borrow trait, but 2:
IntoBorrowhas the ability to give us a type that implementsBorrowfor all lifetimesBorrowwill use this type and give us the actual view Then we can tie both lifetimes to make it valid.
impl<$($view: IntoBorrow + BorrowInfo,)+ R, Sys> IntoWorkloadSystem<($($view,)+), R> for Sys
where
for<'s> Sys:
Fn($($view),+) -> R
+ Fn($(<$view::Borrow as Borrow<'s>>::View),+) -> R
+ 'static
+ Send
+ Sync {
IntoBorrow instead of for<'a> Borrow<'a> and the two bounds on Sys will tie the lifetime of the views from the function's arguments ('s) to the views returned by Borrow.
Reference
If you implement IntoWorkloadSystem like shown above you'll notice that it works but only for references to functions.
I don't know why, so the real implementation is:
impl<$($view: IntoBorrow + BorrowInfo,)+ R, Sys> IntoWorkloadSystem<($($view,)+), R> for Sys
where
Sys: 'static
+ Send
+ Sync,
for<'a, 'b> &'b Sys:
Fn($($view),+) -> R
+ Fn($(<$view::Borrow as Borrow<'a>>::View),+) -> R {
We take the system as value and make sure it's 'static + Send + Sync then in IntoWorkloadSystem implementation we'll take a reference to it.