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:
IntoBorrow
has the ability to give us a type that implementsBorrow
for all lifetimesBorrow
will 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.