diff --git a/README.md b/README.md index 361a10b..112df38 100644 --- a/README.md +++ b/README.md @@ -9,25 +9,33 @@ # ImplicitClone -A library that introduces the marker trait [`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) which allows -reproducing the behavior of the trait [`Copy`][std::marker::Copy] but calls the -[`Clone`][std::clone::Clone] implementation instead and must be implemented in the host -library. +This library introduces the marker trait [`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) intended for +cheap-to-clone types that should be allowed to be cloned implicitly. It enables host libraries +using this crate to have the syntax of [`Copy`][std::marker::Copy] while actually calling the +[`Clone`][std::clone::Clone] implementation instead (usually when host library does such syntax +in a macro). -The idea is that you must implement this trait on types that are cheap to clone -([`std::rc::Rc`][std::rc::Rc] and [`std::sync::Arc`][std::sync::Arc] types are -automatically implemented). Then the host library must use the trait -[`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) to allow their users to pass objects that will be -cloned automatically. +The idea is that you must implement this trait on your cheap-to-clone types, and then the host +library using the trait will allow users to pass values of your types and they will be cloned +automatically. + +Standard types that the [`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) is already implemented for: + +- [`std::rc::Rc`][std::rc::Rc] +- [`std::sync::Arc`][std::sync::Arc] +- Tuples with 1-12 elements, all of which are also [`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) +- [`Option`][std::option::Option], where inner value is [`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) +- Some built-in [`Copy`][std::marker::Copy] types, like `()`, `bool`, `&T`, etc. This crate is in the category `rust-patterns` but this is actually a Rust anti-pattern. In Rust the user should always handle borrowing and ownership by themselves. Nevertheless, this pattern is sometimes desirable. For example, UI frameworks that rely on propagating properties from -ancestors to children will always need to use Rc'ed types to allow every child component to -update. This is the case in React-like framework like Yew. +ancestors to multiple children will always need to use `Rc`'d types to cheaply and concisely +update every child component. This is the case in React-like frameworks like +[Yew](https://yew.rs/). -This crates also provide a few convenient immutable types for handling cheap-to-clone string, -array and maps which you can find in the modules [`sync`](https://docs.rs/implicit-clone/latest/implicit_clone/sync/) and +This crate also provides a few convenient immutable types for handling cheap-to-clone strings, +arrays and maps, you can find them in the modules [`sync`](https://docs.rs/implicit-clone/latest/implicit_clone/sync/) and [`unsync`](https://docs.rs/implicit-clone/latest/implicit_clone/unsync/). Those types implement [`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) and hold only types that implement [`ImplicitClone`](https://docs.rs/implicit-clone/latest/implicit_clone/trait.ImplicitClone.html) as well. **One big particularity: iterating on these types yields clones of the items and not references.** This @@ -37,5 +45,6 @@ can be particularly handy when using a React-like framework. [std::clone::Clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html [std::rc::Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html [std::sync::Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html +[std::option::Option]: https://doc.rust-lang.org/stable/std/option/enum.Option.html diff --git a/src/lib.rs b/src/lib.rs index 9c4a46d..c4b3dfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,25 +7,33 @@ #![cfg_attr(docsrs, feature(doc_cfg))] //! # ImplicitClone //! -//! A library that introduces the marker trait [`ImplicitClone`](crate::ImplicitClone) which allows -//! reproducing the behavior of the trait [`Copy`][std::marker::Copy] but calls the -//! [`Clone`][std::clone::Clone] implementation instead and must be implemented in the host -//! library. +//! This library introduces the marker trait [`ImplicitClone`](crate::ImplicitClone) intended for +//! cheap-to-clone types that should be allowed to be cloned implicitly. It enables host libraries +//! using this crate to have the syntax of [`Copy`][std::marker::Copy] while actually calling the +//! [`Clone`][std::clone::Clone] implementation instead (usually when host library does such syntax +//! in a macro). //! -//! The idea is that you must implement this trait on types that are cheap to clone -//! ([`std::rc::Rc`][std::rc::Rc] and [`std::sync::Arc`][std::sync::Arc] types are -//! automatically implemented). Then the host library must use the trait -//! [`ImplicitClone`](crate::ImplicitClone) to allow their users to pass objects that will be -//! cloned automatically. +//! The idea is that you must implement this trait on your cheap-to-clone types, and then the host +//! library using the trait will allow users to pass values of your types and they will be cloned +//! automatically. +//! +//! Standard types that the [`ImplicitClone`](crate::ImplicitClone) is already implemented for: +//! +//! - [`std::rc::Rc`][std::rc::Rc] +//! - [`std::sync::Arc`][std::sync::Arc] +//! - Tuples with 1-12 elements, all of which are also [`ImplicitClone`](crate::ImplicitClone) +//! - [`Option`][std::option::Option], where inner value is [`ImplicitClone`](crate::ImplicitClone) +//! - Some built-in [`Copy`][std::marker::Copy] types, like `()`, `bool`, `&T`, etc. //! //! This crate is in the category `rust-patterns` but this is actually a Rust anti-pattern. In Rust //! the user should always handle borrowing and ownership by themselves. Nevertheless, this pattern //! is sometimes desirable. For example, UI frameworks that rely on propagating properties from -//! ancestors to children will always need to use Rc'ed types to allow every child component to -//! update. This is the case in React-like framework like Yew. +//! ancestors to multiple children will always need to use `Rc`'d types to cheaply and concisely +//! update every child component. This is the case in React-like frameworks like +//! [Yew](https://yew.rs/). //! -//! This crates also provide a few convenient immutable types for handling cheap-to-clone string, -//! array and maps which you can find in the modules [`sync`](crate::sync) and +//! This crate also provides a few convenient immutable types for handling cheap-to-clone strings, +//! arrays and maps, you can find them in the modules [`sync`](crate::sync) and //! [`unsync`](crate::unsync). Those types implement [`ImplicitClone`](crate::ImplicitClone) and //! hold only types that implement [`ImplicitClone`](crate::ImplicitClone) as well. **One big //! particularity: iterating on these types yields clones of the items and not references.** This @@ -35,18 +43,21 @@ //! [std::clone::Clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html //! [std::rc::Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html //! [std::sync::Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html +//! [std::option::Option]: https://doc.rust-lang.org/stable/std/option/enum.Option.html /// Thread-safe version of immutable types. pub mod sync; /// Single-threaded version of immutable types. pub mod unsync; -/// Marker trait for types that can be cloned implicitly. +/// Marker trait for cheap-to-clone types that should be allowed to be cloned implicitly. /// -/// Behaves exactly like [`Copy`] but calls the [`Clone`] implementation instead and must be -/// implemented in the host library. +/// Enables host libraries to have the same syntax as [`Copy`] while calling the [`Clone`] +/// implementation instead. pub trait ImplicitClone: Clone {} +impl ImplicitClone for &T {} + impl ImplicitClone for Option {} macro_rules! impl_implicit_clone { @@ -62,7 +73,7 @@ impl_implicit_clone!( f32, f64, bool, usize, isize, - &'static str, char, + char, (), ); @@ -119,3 +130,79 @@ macro_rules! imap_deconstruct { )* }; } + +#[cfg(test)] +mod test { + use super::*; + + fn host_library(value: &T) -> T { + value.clone() + } + + macro_rules! host_library { + ($a:expr) => { + host_library(&$a) + }; + } + + struct NonImplicitCloneType; + + #[test] + fn custom() { + #[derive(Clone)] + struct ImplicitCloneType; + + impl ImplicitClone for ImplicitCloneType {} + + host_library!(ImplicitCloneType); + } + + #[test] + fn copy_types() { + macro_rules! test_all { + ($($t:ty),* $(,)?) => { + $(host_library!(<$t>::default());)* + }; + } + + #[rustfmt::skip] + test_all!( + u8, u16, u32, u64, u128, + i8, i16, i32, i64, i128, + f32, f64, + bool, + usize, isize, char, + (), + ); + } + + #[test] + fn ref_type() { + host_library!(&NonImplicitCloneType); + // `host_library!(NonImplicitCloneType)` doesn't compile + } + + #[test] + fn option() { + host_library!(Some("foo")); + // `host_library!(Some(NonImplicitCloneType));` doesn't compile + } + + #[test] + fn tuples() { + host_library!((1,)); + host_library!((1, 2)); + host_library!((1, 2, 3)); + host_library!((1, 2, 3, 4)); + host_library!((1, 2, 3, 4, 5)); + host_library!((1, 2, 3, 4, 5, 6)); + host_library!((1, 2, 3, 4, 5, 6, 7)); + host_library!((1, 2, 3, 4, 5, 6, 7, 8)); + host_library!((1, 2, 3, 4, 5, 6, 7, 8, 9)); + host_library!((1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + host_library!((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + host_library!((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); + // `host_library!((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13));` doesn't compile + // `host_library!((NonImplicitCloneType,));` doesn't compile + } +}