0.4 comes with a few big changes, this chapter aims to facilitate the transition.
## Imports
Let's start small, prelude and internal no longer exist, you just have to replace all `shipyard::prelude` and `shipyard::internal` by `shipyard`.
## Systems
Following [an issue](https://github.com/leudz/shipyard/issues/75) opened by [@cart](https://github.com/cart), systems will become functions instead of an instance of the `System` trait.
To make this work, borrowing is now done with the actual types you get when you borrow a storage instead of using references.
In 0.3:
```rust, noplaypen
struct MySystem;
impl<'sys> System<'sys> for MySystem {
type Data = (
EntitiesMut,
&mut usize,
);
fn run((mut entities, mut usizes): <Self::Data as SystemData<'sys>>::View) {}
}
// or with the macro
#[system(MySystem)]
fn run(mut entities: &mut Entities, mut usizes: &mut usize) {}
```
In 0.4:
```rust, noplaypen
fn my_system(mut entities: EntitiesViewMut, mut usizes: ViewMut<usize>) {}
```
This change also affects `run` and `borrow`.
`World::run_system` is no longer needed and you can run systems with `run` directly.
```rust, noplaypen
world.run(my_system);
// and closures still work
`run` has the same return type as the system or closure and it doesn't require any tuple most of the time.
Here's the complete list:
`AllStorages` / `&mut AllStorages`|[`AllStoragesViewMut`](https://docs.rs/shipyard/latest/shipyard/struct.AllStoragesViewMut.html)
`Entities` / `&Entities`|[`EntitiesView`](https://docs.rs/shipyard/latest/shipyard/struct.EntitiesView.html)
`EntitiesMut` / `&mut Entities`|[`EntitiesViewMut`](https://docs.rs/shipyard/latest/shipyard/struct.EntitiesViewMut.html)
`&T`|[`View<T>`](https://docs.rs/shipyard/latest/shipyard/struct.View.html)
`&mut T`|[`ViewMut<T>`](https://docs.rs/shipyard/latest/shipyard/struct.ViewMut.html)
`ThreadPool` / `&ThreadPool`|[`ThreadPoolView`](https://docs.rs/shipyard/latest/shipyard/struct.ThreadPoolView.html)
`Unique<&T>`|[`UniqueView<T>`](https://docs.rs/shipyard/latest/shipyard/struct.UniqueView.html)
`Unique<&mut T>`|[`UniqueViewMut<T>`](https://docs.rs/shipyard/latest/shipyard/struct.UniqueViewMut.html)
`NonSend<&T>`|[`NonSend<View<T>>`](https://docs.rs/shipyard/latest/shipyard/struct.NonSend.html)
`NonSend<&mut T>`|[`NonSend<ViewMut<T>>`](https://docs.rs/shipyard/latest/shipyard/struct.NonSend.html)
`Unique<NonSend<&T>>`|[`NonSend<UniqueView<T>>`](https://docs.rs/shipyard/latest/shipyard/struct.NonSend.html)
`Unique<NonSend<&mut T>>`|[`NonSend<UniqueViewMut<T>>`](https://docs.rs/shipyard/latest/shipyard/struct.NonSend.html)
`FakeBorrow<T>`|[`FakeBorrow<T>`](https://docs.rs/shipyard/latest/shipyard/struct.FakeBorrow.html)
[`NonSync`](https://docs.rs/shipyard/latest/shipyard/struct.NonSync.html) and [`NonSendSync`](https://docs.rs/shipyard/latest/shipyard/struct.NonSendSync.html) follow the same pattern as `NonSend`.
## Macro
The system proc macro doesn't exist anymore. With the new system design the advantage it provides are not great enough to justify it.
## Workloads
### The ugly
Workloads are the only one suffering a downgrade. You'll have to give all systems twice to the function plus a few things.
In 0.3:
```rust, noplaypen
world.add_workload<(Sys1, Sys2), _>("Workload1");
```
In 0.4:
```rust, noplaypen
world
.add_workload("Workload1")
.with_system((
|world: &World| world.try_run(sys1),
sys1
))
.with_system((
|world: &World| world.try_run(sys2),
sys2
))
.build();
// with a macro
world
.add_workload("Workload1")
.with_system(system!(sys1))
.with_system(system!(sys2))
.build();
```
⚠️ The two arguments are wrapped in a tuple.
This repetition will disappear someday but I don't expect it to be soon.
You don't have to use a closure, any function with `&World` as argument and returning `Result<(), shipyard::error::Run>` are valid.
It's very important to pass the same function twice, the workload might always error if this isn't the case.
### The good
Workloads don't come with only a downgrade. You can now return errors from systems inside workloads.
```rust, noplaypen
#[derive(Debug)]
struct TerribleError;
impl Display for TerribleError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
Debug::fmt(self, fmt)
}
}
impl Error for TerribleError {}
fn my_sys(mut entities: EntitiesViewMut) -> Result<(), TerribleError> {
Err(TerribleError)
}
fn main() {
use shipyard::error::{Run, RunWorkload};
let world = World::new();
world
.add_workload("May fail")
.with_system((
|world: &World| world.try_run(my_sys)?.map_err(Run::from_custom),
my_sys,
))
.build();
match world.try_run_default().map_err(RunWorkload::custom_error) {
Err(Some(error)) => {
assert!(error.is::<TerribleError>());
}
_ => {}
}
}
```
The error has to be anonymized so you'll get back a `Box<dyn Error + Send>` with std and a `Box<dyn Any + Send>` with no_std.
Workloads stop at the first error encountered, just like 0.3.
You can also use the `try_system!` macro the same way as `system!`.
```rust, noplaypen
world
.add_workload("May fail")
.with_system(try_system!(my_sys))
.build();
```
It'll generate the same code as above.
## Iterator
You can now use `std::iter::Iterator` and `for loop` with views without having to call `into_iter`.
All iteration code from 0.3 will still work.
```rust, noplaypen
fn my_sys((mut usizes, u32s): (ViewMut<usize>, View<u32>)) {
for (i, &j) in (&mut usizes, &u32s).iter() {
*i += j as usize;
}
}
```
## Get
The `GetComponent` trait has been renamed `Get`.
What follows is only true for 0.4. 0.5 went back to `get` returning a `Result`.
`Get::get` has been renamed `try_get` and a new `get` method has been added to unwrap errors.
If you used `get` followed by `unwrap`, you can simply remove the `unwrap`.
If you used another error handling method you'll have to replace `get` by `try_get`.