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 opened by @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:

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:

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.

world.run(my_system);

// and closures still work

world.run(|mut entities: EntitiesViewMut, mut usizes: ViewMut<usize>| {});

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:

0.30.4
AllStorages / &mut AllStoragesAllStoragesViewMut
Entities / &EntitiesEntitiesView
EntitiesMut / &mut EntitiesEntitiesViewMut
&TView<T>
&mut TViewMut<T>
ThreadPool / &ThreadPoolThreadPoolView
Unique<&T>UniqueView<T>
Unique<&mut T>UniqueViewMut<T>
NonSend<&T>NonSend<View<T>>
NonSend<&mut T>NonSend<ViewMut<T>>
Unique<NonSend<&T>>NonSend<UniqueView<T>>
Unique<NonSend<&mut T>>NonSend<UniqueViewMut<T>>
FakeBorrow<T>FakeBorrow<T>

NonSync and NonSendSync 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:

world.add_workload<(Sys1, Sys2), _>("Workload1");

In 0.4:

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.

#[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!.

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.

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.