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//! This library introduces the marker trait [`ImplicitClone`](crate::ImplicitClone) intended for
12//! cheap-to-clone types that should be allowed to be cloned implicitly. It enables host libraries
13//! using this crate to have the syntax of [`Copy`][std::marker::Copy] while actually calling the
14//! [`Clone`][std::clone::Clone] implementation instead (usually when host library does such syntax
15//! in a macro).
16//!
17//! The idea is that you must implement this trait on your cheap-to-clone types, and then the host
18//! library using the trait will allow users to pass values of your types and they will be cloned
19//! automatically.
20//!
21//! Standard types that the [`ImplicitClone`](crate::ImplicitClone) is already implemented for:
22//!
23//! - [`std::rc::Rc`][std::rc::Rc]
24//! - [`std::sync::Arc`][std::sync::Arc]
25//! - Tuples with 1-12 elements, all of which are also [`ImplicitClone`](crate::ImplicitClone)
26//! - [`Option`][std::option::Option], where inner value is [`ImplicitClone`](crate::ImplicitClone)
27//! - Some built-in [`Copy`][std::marker::Copy] types, like `()`, `bool`, `&T`, etc.
28//!
29//! This crate is in the category `rust-patterns` but this is actually a Rust anti-pattern. In Rust
30//! the user should always handle borrowing and ownership by themselves. Nevertheless, this pattern
31//! is sometimes desirable. For example, UI frameworks that rely on propagating properties from
32//! ancestors to multiple children will always need to use `Rc`'d types to cheaply and concisely
33//! update every child component. This is the case in React-like frameworks like
34//! [Yew](https://yew.rs/).
35//!
36//! This crate also provides a few convenient immutable types for handling cheap-to-clone strings,
37//! arrays and maps, you can find them in the modules [`sync`](crate::sync) and
38//! [`unsync`](crate::unsync). Those types implement [`ImplicitClone`](crate::ImplicitClone) and
39//! hold only types that implement [`ImplicitClone`](crate::ImplicitClone) as well. **One big
40//! particularity: iterating on these types yields clones of the items and not references.** This
41//! can be particularly handy when using a React-like framework.
42//!
43//! ## Example
44//!
45//! As an example, here is an implementation of a macro called `html_input! {}` which allows its
46//! user to build an `<input>` HTML node:
47//!
48//! ```
49//! // In the host library source code:
50//!
51//! use implicit_clone::ImplicitClone;
52//! use implicit_clone::unsync::{IArray, IString};
53//!
54//! macro_rules! html_input {
55//! (<input $(type={$ty:expr})? $(name={$name:expr})? $(value={$value:expr})?>) => {{
56//! let mut input = Input::new();
57//! $(input.set_type($ty);)*
58//! $(input.set_name($name);)*
59//! $(input.set_value($value);)*
60//! input
61//! }}
62//! }
63//!
64//! #[derive(Clone)]
65//! pub struct Input {
66//! ty: IString,
67//! name: Option<IString>,
68//! value: Option<IString>,
69//! }
70//!
71//! impl ImplicitClone for Input {}
72//!
73//! impl Input {
74//! pub fn new() -> Self {
75//! Self {
76//! ty: IString::Static("text"),
77//! name: None,
78//! value: None,
79//! }
80//! }
81//!
82//! pub fn set_type(&mut self, ty: impl Into<IString>) {
83//! self.ty = ty.into();
84//! }
85//!
86//! pub fn set_name(&mut self, name: impl Into<IString>) {
87//! self.name.replace(name.into());
88//! }
89//!
90//! pub fn set_value(&mut self, value: impl Into<IString>) {
91//! self.value.replace(value.into());
92//! }
93//! }
94//!
95//! impl std::fmt::Display for Input {
96//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97//! write!(f, "<input type=\"{}\"", self.ty)?;
98//! if let Some(name) = self.name.as_ref() {
99//! write!(f, " name=\"{}\"", name)?;
100//! }
101//! if let Some(value) = self.value.as_ref() {
102//! write!(f, " value=\"{}\"", value)?;
103//! }
104//! write!(f, ">")
105//! }
106//! }
107//!
108//! // In the user's source code:
109//!
110//! fn component(age: &IString) -> IArray<Input> {
111//! // `age` is implicitly cloned to the 2 different inputs
112//! let input1 = html_input!(<input name={"age"} value={age}>);
113//! let input2 = html_input!(<input name={"age"} value={age}>);
114//!
115//! IArray::from(vec![input1, input2])
116//! }
117//!
118//! let age = IString::from(20.to_string());
119//! let output = component(&age);
120//! let output_str = output
121//! .iter()
122//! .map(|x| x.to_string())
123//! .collect::<Vec<_>>()
124//! .join("");
125//!
126//! assert_eq!(
127//! output_str,
128//! r#"<input type="text" name="age" value="20"><input type="text" name="age" value="20">"#,
129//! );
130//! ```
131//!
132//! [std::marker::Copy]: https://doc.rust-lang.org/std/marker/trait.Copy.html
133//! [std::clone::Clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html
134//! [std::rc::Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html
135//! [std::sync::Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html
136//! [std::option::Option]: https://doc.rust-lang.org/stable/std/option/enum.Option.html
137
138/// Thread-safe version of immutable types.
139pub mod sync;
140/// Single-threaded version of immutable types.
141pub mod unsync;
142
143#[cfg(feature = "implicit-clone-derive")]
144pub use implicit_clone_derive::*;
145
146/// Marker trait for cheap-to-clone types that should be allowed to be cloned implicitly.
147///
148/// Enables host libraries to have the same syntax as [`Copy`] while calling the [`Clone`]
149/// implementation instead.
150pub trait ImplicitClone: Clone {
151 /// This function is not magic; it is literally defined as
152 ///
153 /// ```ignore
154 /// fn implicit_clone(&self) -> Self {
155 /// self.clone()
156 /// }
157 /// ```
158 ///
159 /// It is useful when you want to clone but also ensure that the type implements
160 /// [`ImplicitClone`].
161 ///
162 /// Examples:
163 ///
164 /// ```
165 /// use implicit_clone::ImplicitClone;
166 /// let x: u32 = Default::default();
167 /// let clone = ImplicitClone::implicit_clone(&x);
168 /// ```
169 ///
170 /// ```compile_fail
171 /// use implicit_clone::ImplicitClone;
172 /// let x: Vec<u32> = Default::default();
173 /// // does not compile because Vec<_> does not implement ImplicitClone
174 /// let clone = ImplicitClone::implicit_clone(&x);
175 /// ```
176 #[inline]
177 fn implicit_clone(&self) -> Self {
178 self.clone()
179 }
180}
181
182impl<T: ?Sized> ImplicitClone for &T {}
183
184impl<T: ImplicitClone> ImplicitClone for Option<T> {}
185
186macro_rules! impl_implicit_clone {
187 ($($ty:ty),+ $(,)?) => {
188 $(impl ImplicitClone for $ty {})*
189 };
190}
191
192#[rustfmt::skip]
193impl_implicit_clone!(
194 u8, u16, u32, u64, u128,
195 i8, i16, i32, i64, i128,
196 f32, f64,
197 bool,
198 usize, isize,
199 char,
200 (),
201);
202
203impl<const N: usize, T: ImplicitClone> ImplicitClone for [T; N] {}
204
205macro_rules! impl_implicit_clone_for_tuple {
206 ($($param:ident),+ $(,)?) => {
207 impl<$($param: ImplicitClone),+> ImplicitClone for ($($param,)+) {}
208 };
209}
210
211impl_implicit_clone_for_tuple!(T1,);
212impl_implicit_clone_for_tuple!(T1, T2);
213impl_implicit_clone_for_tuple!(T1, T2, T3);
214impl_implicit_clone_for_tuple!(T1, T2, T3, T4);
215impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5);
216impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6);
217impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7);
218impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
219impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
220impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
221impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
222impl_implicit_clone_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
223
224/// A macro to help deconstructs maps inspired by JS.
225///
226/// This macro is an experiment and may change or be entirely deleted before the 1.0 release.
227///
228/// # Usage
229///
230/// ```rust
231/// use implicit_clone::unsync::*;
232/// use implicit_clone::imap_deconstruct;
233///
234/// let my_imap = [(IString::from("foo"), 1), (IString::from("bar"), 2)]
235/// .into_iter()
236/// .collect::<IMap<IString, u32>>();
237/// imap_deconstruct!(
238/// let { foo, bar, baz } = my_imap;
239/// let { foobarbaz } = my_imap;
240/// );
241/// assert_eq!(foo, Some(1));
242/// assert_eq!(bar, Some(2));
243/// assert_eq!(baz, None);
244/// assert_eq!(foobarbaz, None);
245/// ```
246#[cfg(feature = "map")]
247#[cfg_attr(docsrs, doc(cfg(feature = "map")))]
248#[macro_export]
249macro_rules! imap_deconstruct {
250 ($(let { $($key:ident),+ $(,)? } = $map:expr;)*) => {
251 $(
252 $(
253 let $key = $map.get_static_str(stringify!($key));
254 )*
255 )*
256 };
257}
258
259#[cfg(test)]
260mod test {
261 use super::*;
262 use static_assertions::*;
263
264 #[test]
265 fn custom() {
266 #[derive(Clone)]
267 #[allow(dead_code)]
268 struct ImplicitCloneType;
269
270 impl ImplicitClone for ImplicitCloneType {}
271
272 assert_impl_all!(ImplicitCloneType: ImplicitClone);
273 }
274
275 #[test]
276 fn copy_types() {
277 macro_rules! test_all {
278 ($($t:ty),* $(,)?) => {
279 $(assert_impl_all!($t: ImplicitClone, Copy);)*
280 };
281 }
282
283 #[rustfmt::skip]
284 test_all!(
285 u8, u16, u32, u64, u128,
286 i8, i16, i32, i64, i128,
287 f32, f64,
288 bool,
289 usize, isize, char,
290 (),
291 [u8; 4],
292 &[u8],
293 );
294 }
295
296 #[test]
297 fn ref_type() {
298 assert_impl_all!(&Vec<u8>: ImplicitClone);
299 assert_not_impl_all!(Vec<u8>: ImplicitClone);
300 }
301
302 #[test]
303 fn option() {
304 assert_impl_all!(Option<&'static str>: ImplicitClone);
305 assert_not_impl_all!(Option<Vec<u8>>: ImplicitClone);
306 }
307
308 #[test]
309 fn tuples() {
310 assert_impl_all!((u8,): ImplicitClone);
311 assert_impl_all!((u8, u8): ImplicitClone);
312 assert_impl_all!((u8, u8, u8): ImplicitClone);
313 assert_impl_all!((u8, u8, u8, u8): ImplicitClone);
314 assert_impl_all!((u8, u8, u8, u8, u8): ImplicitClone);
315 assert_impl_all!((u8, u8, u8, u8, u8, u8): ImplicitClone);
316 assert_impl_all!((u8, u8, u8, u8, u8, u8, u8): ImplicitClone);
317 assert_impl_all!((u8, u8, u8, u8, u8, u8, u8, u8): ImplicitClone);
318 assert_impl_all!((u8, u8, u8, u8, u8, u8, u8, u8, u8): ImplicitClone);
319 assert_impl_all!((u8, u8, u8, u8, u8, u8, u8, u8, u8, u8): ImplicitClone);
320 assert_impl_all!((u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8): ImplicitClone);
321 assert_impl_all!((u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8): ImplicitClone);
322 assert_not_impl_all!((u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8): ImplicitClone);
323 assert_not_impl_all!((Vec<u8>,): ImplicitClone);
324 }
325}