Crate implicit_clone
source ·Expand description
§ImplicitClone
This library introduces the marker trait 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
while actually calling the
Clone
implementation instead (usually when host library does such syntax
in a macro).
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
is already implemented for:
std::rc::Rc
std::sync::Arc
- Tuples with 1-12 elements, all of which are also
ImplicitClone
Option
, where inner value isImplicitClone
- Some built-in
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 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.
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
and
unsync
. Those types implement ImplicitClone
and
hold only types that implement ImplicitClone
as well. One big
particularity: iterating on these types yields clones of the items and not references. This
can be particularly handy when using a React-like framework.
§Example
As an example, here is an implementation of a macro called html_input! {}
which allows its
user to build an <input>
HTML node:
// In the host library source code:
use implicit_clone::ImplicitClone;
use implicit_clone::unsync::{IArray, IString};
macro_rules! html_input {
(<input $(type={$ty:expr})? $(name={$name:expr})? $(value={$value:expr})?>) => {{
let mut input = Input::new();
$(input.set_type($ty);)*
$(input.set_name($name);)*
$(input.set_value($value);)*
input
}}
}
#[derive(Clone)]
pub struct Input {
ty: IString,
name: Option<IString>,
value: Option<IString>,
}
impl ImplicitClone for Input {}
impl Input {
pub fn new() -> Self {
Self {
ty: IString::Static("text"),
name: None,
value: None,
}
}
pub fn set_type(&mut self, ty: impl Into<IString>) {
self.ty = ty.into();
}
pub fn set_name(&mut self, name: impl Into<IString>) {
self.name.replace(name.into());
}
pub fn set_value(&mut self, value: impl Into<IString>) {
self.value.replace(value.into());
}
}
impl std::fmt::Display for Input {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<input type=\"{}\"", self.ty)?;
if let Some(name) = self.name.as_ref() {
write!(f, " name=\"{}\"", name)?;
}
if let Some(value) = self.value.as_ref() {
write!(f, " value=\"{}\"", value)?;
}
write!(f, ">")
}
}
// In the user's source code:
fn component(age: &IString) -> IArray<Input> {
// `age` is implicitly cloned to the 2 different inputs
let input1 = html_input!(<input name={"age"} value={age}>);
let input2 = html_input!(<input name={"age"} value={age}>);
IArray::from(vec![input1, input2])
}
let age = IString::from(20.to_string());
let output = component(&age);
let output_str = output
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join("");
assert_eq!(
output_str,
r#"<input type="text" name="age" value="20"><input type="text" name="age" value="20">"#,
);
Modules§
- Thread-safe version of immutable types.
- Single-threaded version of immutable types.
Macros§
- A macro to help deconstructs maps inspired by JS.
Traits§
- Marker trait for cheap-to-clone types that should be allowed to be cloned implicitly.