From c16791ce6712854f031e79ebc303fb6bb540912b Mon Sep 17 00:00:00 2001 From: James Liu Date: Sun, 11 Dec 2022 18:34:13 +0000 Subject: [PATCH] Document options for !Sync types for Component and Resources (#6864) # Objective It's not clear to users how to handle `!Sync` types as components and resources in the absence of engine level support for them. ## Solution Added a section to `Component`'s and `Resource`'s type level docs on available options for making a type `Sync` when it holds `!Sync` fields, linking `bevy_utils::synccell::SyncCell` and the currently unstable `std::sync::Exclusive`. Also added a compile_fail doctest that illustrates how to apply `SyncCell`. These will break when/if #6572 gets merged, at which point these docs should be updated. --- crates/bevy_ecs/src/component.rs | 32 ++++++++++++++++++++++ crates/bevy_ecs/src/system/system_param.rs | 32 ++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 442fc60ae0..39f89b95ff 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -110,6 +110,38 @@ use std::{ /// /// [orphan rule]: https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type /// [newtype pattern]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types +/// +/// # `!Sync` Components +/// A `!Sync` type cannot implement `Component`. However, it is possible to wrap a `Send` but not `Sync` +/// type in [`SyncCell`] or the currently unstable [`Exclusive`] to make it `Sync`. This forces only +/// having mutable access (`&mut T` only, never `&T`), but makes it safe to reference across multiple +/// threads. +/// +/// This will fail to compile since `RefCell` is `!Sync`. +/// ```compile_fail +/// # use std::cell::RefCell; +/// # use bevy_ecs::component::Component; +/// #[derive(Component)] +/// struct NotSync { +/// counter: RefCell, +/// } +/// ``` +/// +/// This will compile since the `RefCell` is wrapped with `SyncCell`. +/// ``` +/// # use std::cell::RefCell; +/// # use bevy_ecs::component::Component; +/// use bevy_utils::synccell::SyncCell; +/// +/// // This will compile. +/// #[derive(Component)] +/// struct ActuallySync { +/// counter: SyncCell>, +/// } +/// ``` +/// +/// [`SyncCell`]: bevy_utils::synccell::SyncCell +/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html pub trait Component: Send + Sync + 'static { type Storage: ComponentStorage; } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index a10737acad..d52961c4ad 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -89,6 +89,38 @@ use std::{ /// /// This will most commonly occur when working with `SystemParam`s generically, as the requirement /// has not been proven to the compiler. +/// +/// # `!Sync` Resources +/// A `!Sync` type cannot implement `Resource`. However, it is possible to wrap a `Send` but not `Sync` +/// type in [`SyncCell`] or the currently unstable [`Exclusive`] to make it `Sync`. This forces only +/// having mutable access (`&mut T` only, never `&T`), but makes it safe to reference across multiple +/// threads. +/// +/// This will fail to compile since `RefCell` is `!Sync`. +/// ```compile_fail +/// # use std::cell::RefCell; +/// # use bevy_ecs::system::Resource; +/// +/// #[derive(Resource)] +/// struct NotSync { +/// counter: RefCell, +/// } +/// ``` +/// +/// This will compile since the `RefCell` is wrapped with `SyncCell`. +/// ``` +/// # use std::cell::RefCell; +/// # use bevy_ecs::system::Resource; +/// use bevy_utils::synccell::SyncCell; +/// +/// #[derive(Resource)] +/// struct ActuallySync { +/// counter: SyncCell>, +/// } +/// ``` +/// +/// [`SyncCell`]: bevy_utils::synccell::SyncCell +/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html pub trait SystemParam: Sized { type Fetch: for<'w, 's> SystemParamFetch<'w, 's>; }