implicit_clone/lib.rs
1#![warn(missing_debug_implementations, missing_docs, unreachable_pub)]
2#![allow(clippy::unnecessary_lazy_evaluations)]
3#![allow(clippy::duplicate_mod)]
4#![doc(test(
5 no_crate_inject,
6 attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
7))]
8#![cfg_attr(docsrs, feature(doc_cfg))]
9//! # ImplicitClone
10//!
11//! A library that introduces the marker trait [`ImplicitClone`](crate::ImplicitClone) which allows
12//! reproducing the behavior of the trait [`Copy`][std::marker::Copy] but calls the
13//! [`Clone`][std::clone::Clone] implementation instead and must be implemented in the host
14//! library.
15//!
16//! The idea is that you must implement this trait on types that are cheap to clone
17//! ([`std::rc::Rc`][std::rc::Rc] and [`std::sync::Arc`][std::sync::Arc] types are
18//! automatically implemented). Then the host library must use the trait
19//! [`ImplicitClone`](crate::ImplicitClone) to allow their users to pass objects that will be
20//! cloned automatically.
21//!
22//! This crate is in the category `rust-patterns` but this is actually a Rust anti-pattern. In Rust
23//! the user should always handle borrowing and ownership by themselves. Nevertheless, this pattern
24//! is sometimes desirable. For example, UI frameworks that rely on propagating properties from
25//! ancestors to children will always need to use Rc'ed types to allow every child component to
26//! update. This is the case in React-like framework like Yew.
27//!
28//! This crates also provide a few convenient immutable types for handling cheap-to-clone string,
29//! array and maps which you can find in the modules [`sync`](crate::sync) and
30//! [`unsync`](crate::unsync). Those types implement [`ImplicitClone`](crate::ImplicitClone) and
31//! hold only types that implement [`ImplicitClone`](crate::ImplicitClone) as well. **One big
32//! particularity: iterating on these types yields clones of the items and not references.** This
33//! can be particularly handy when using a React-like framework.
34//!
35//! [std::marker::Copy]: https://doc.rust-lang.org/std/marker/trait.Copy.html
36//! [std::clone::Clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html
37//! [std::rc::Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html
38//! [std::sync::Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html
39
40/// Thread-safe version of immutable types.
41pub mod sync;
42/// Single-threaded version of immutable types.
43pub mod unsync;
44
45/// Marker trait for types that can be cloned implicitly.
46///
47/// Behaves exactly like [`Copy`] but calls the [`Clone`] implementation instead and must be
48/// implemented in the host library.
49pub trait ImplicitClone: Clone {}
50
51impl<T: ImplicitClone> ImplicitClone for Option<T> {}
52
53macro_rules! impl_implicit_clone {
54 ($($ty:ty),+ $(,)?) => {
55 $(impl ImplicitClone for $ty {})*
56 };
57}
58
59#[rustfmt::skip]
60impl_implicit_clone!(
61 u8, u16, u32, u64, u128,
62 i8, i16, i32, i64, i128,
63 f32, f64,
64 bool,
65 usize, isize,
66 &'static str, char,
67 (),
68);
69
70macro_rules! impl_implicit_clone_for_tuple {
71 ($($param:ident),+ $(,)?) => {
72 impl<$($param: ImplicitClone),+> ImplicitClone for ($($param,)+) {}
73 };
74}
75
76impl_implicit_clone_for_tuple!(T1,);
77impl_implicit_clone_for_tuple!(T1, T2);
78impl_implicit_clone_for_tuple!(T1, T2, T3);
79impl_implicit_clone_for_tuple!(T1, T2, T3, T4);
80impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5);
81impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6);
82impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7);
83impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
84impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
85impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
86impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
87impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
88
89/// A macro to help deconstructs maps inspired by JS.
90///
91/// This macro is an experiment and may change or be entirely deleted before the 1.0 release.
92///
93/// # Usage
94///
95/// ```rust
96/// use implicit_clone::unsync::*;
97/// use implicit_clone::imap_deconstruct;
98///
99/// let my_imap = [(IString::from("foo"), 1), (IString::from("bar"), 2)]
100/// .into_iter()
101/// .collect::<IMap<IString, u32>>();
102/// imap_deconstruct!(
103/// let { foo, bar, baz } = my_imap;
104/// let { foobarbaz } = my_imap;
105/// );
106/// assert_eq!(foo, Some(1));
107/// assert_eq!(bar, Some(2));
108/// assert_eq!(baz, None);
109/// assert_eq!(foobarbaz, None);
110/// ```
111#[cfg(feature = "map")]
112#[cfg_attr(docsrs, doc(cfg(feature = "map")))]
113#[macro_export]
114macro_rules! imap_deconstruct {
115 ($(let { $($key:ident),+ $(,)? } = $map:expr;)*) => {
116 $(
117 $(
118 let $key = $map.get_static_str(stringify!($key));
119 )*
120 )*
121 };
122}