wiwi/macro_util.rs
1extern crate wiwi_macro_decl;
2extern crate wiwi_macro_proc;
3
4pub use wiwi_macro_decl::macro_recurse;
5pub use wiwi_macro_decl::void;
6
7/// Executes code with cloned values, so the executed code
8/// can take ownership of the value without moving the original
9///
10/// Read on if you want to know why I wrote this smol macro and what problems
11/// it's designed to fix, but for those just wanting to use the thing:
12///
13/// ```
14/// # use wiwi::macro_util::with_cloned;
15/// let s1 = String::from("a string");
16/// let s2 = String::from("another string");
17///
18/// let new = with_cloned! { mut s1, s2 in
19/// // you may specify one or more variables to be cloned in a comma-seperated list
20/// // s1 and s2 are cloned in here, and you can optionally specify `mut`
21///
22/// // do something with the cloned values, using any arbitrary code...
23/// let mut joined = s2; // moves `s2`
24/// joined.push_str(&s1);
25/// // demonstrate that it is indeed a clone
26/// drop::<String>(s1); // moves `s1`
27///
28/// // you can also "return" values out of the macro
29/// joined
30/// };
31///
32/// // originals are not modified or moved out,
33/// // despite the bindings within the macro being moved
34/// assert_eq!(s1, "a string");
35/// assert_eq!(s2, "another string");
36///
37/// // returned value from macro
38/// assert_eq!(new, "another stringa string");
39/// ```
40///
41/// Now onto ramble. Let's say you have some code...
42///
43/// ```
44/// # use std::sync::{ Arc, Mutex };
45/// # use std::thread;
46/// let shared_data = Arc::new(Mutex::new(42));
47///
48/// // send a copy to another thread to concurrently mutate...
49/// let _shared_data = shared_data.clone();
50/// let join_handle = thread::spawn(move || {
51/// // do something with this data...
52/// *_shared_data.lock().unwrap() *= 2;
53/// });
54///
55/// // ... while keeping one to use
56/// *shared_data.lock().unwrap() *= 5;
57///
58/// join_handle.join().unwrap();
59// ...wait, this was not intentional lol
60/// assert_eq!(*shared_data.lock().unwrap(), 420);
61/// ```
62///
63/// ... this is not great, since you'd have to use `_shared_data`, which is
64/// different from the original one, and the underscore might look kind of ugly,
65/// and it's just not ideal.
66///
67/// It can also be done with a temporary scope:
68///
69/// ```
70/// # use std::sync::{ Arc, Mutex };
71/// # use std::thread;
72/// # let shared_data = Arc::new(Mutex::new(42));
73/// let join_handle = {
74/// let shared_data = shared_data.clone();
75/// thread::spawn(move || {
76/// *shared_data.lock().unwrap() *= 2;
77/// })
78/// };
79///
80/// *shared_data.lock().unwrap() *= 5;
81///
82/// join_handle.join().unwrap();
83/// assert_eq!(*shared_data.lock().unwrap(), 420);
84/// ```
85///
86/// ... not really what we would do (really only minor style preferences, nothing
87/// actually relating to function), but still has some boilerplate, and not the
88/// most optimal either way.
89///
90/// In our short(...ish?) time writing rust code, I've already noticed I use this
91/// pattern a lot when dealing with shared state across threads. There are likely
92/// patterns that we don't know about, that would do something similar here (clone
93/// a value, then consume the clone, then access original). Or maybe we do know
94/// about them, but we haven't thought about it at time of writing this documentation
95/// :p. Needing to write all that boilerplate code for an otherwise very simple
96/// operation, doesn't feel very ergonomic or elegant, and quickly gets repetitive.
97///
98/// This macro can help with that:
99///
100/// ```
101/// # use std::sync::{ Arc, Mutex };
102/// # use std::thread;
103/// # use wiwi::macro_util::with_cloned;
104/// # let shared_data = Arc::new(Mutex::new(42));
105/// let join_handle = with_cloned! { shared_data in
106/// // `shared_data` in here will refer to a clone, shadowing the original,
107/// // and I can do whatever, including taking ownership of it
108/// thread::spawn(move || {
109/// *shared_data.lock().unwrap() *= 2;
110/// })
111/// };
112///
113/// // original is not touched, this is no problem
114/// *shared_data.lock().unwrap() *= 5;
115///
116/// join_handle.join().unwrap();
117/// assert_eq!(*shared_data.lock().unwrap(), 420);
118/// ```
119///
120/// It cut out most of the boilerplate, and just feels a lot nicer to use.
121///
122/// The syntax of the macro is, first a (comma seperated) list of the variables
123/// that should have a cloned version available, followed by keyword `in`, then
124/// any statements you would like to run. Essentially, you can think of the stuff
125/// after `in` as just a regular block. Those who know what [swift closures] look
126/// like may recognise this syntax hehe
127///
128/// [swift closures]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures#Closure-Expression-Syntax
129///
130/// You can also bind the cloned values mutably, as well as return values out
131/// of the macro:
132///
133/// ```
134/// # use wiwi::macro_util::with_cloned;
135/// let string = String::new();
136///
137/// let modified_string = with_cloned! { mut string in
138/// // ^^^
139///
140/// // do whatever
141/// string.push_str("uwu");
142///
143/// assert_eq!(string, "uwu");
144/// string
145/// };
146///
147/// // original is untounched
148/// assert_eq!(string, "");
149///
150/// // returned the modified version from within the macro
151/// assert_eq!(modified_string, "uwu");
152/// ```
153///
154/// Ideally you could bind variables mutably or not on a per variable basis,
155/// but unfortunately, I can't seem to find a way to do so elegantly >~< so it's
156/// all or nothing for now.
157///
158/// Random, but the below code snippets are equivalent:
159///
160/// ```
161/// # use wiwi::macro_util::with_cloned;
162/// # let value = String::new();
163/// let cloned1 = value.clone();
164/// // macro clones it for the inner code, but the inner code
165/// // just returns it... so it is just a clone
166/// let cloned2 = with_cloned! { value in value };
167///
168/// assert_eq!(value, cloned1);
169/// assert_eq!(value, cloned2);
170/// ```
171#[doc(inline)]
172pub use wiwi_macro_decl::with_cloned;
173
174pub use wiwi_macro_decl::with_cloned_2;
175pub use wiwi_macro_decl::with_cloned_3;
176pub use wiwi_macro_decl::with_cloned_4;
177pub use wiwi_macro_proc::with_cloned as with_cloned_proc;