variadiz/
lib.rs

1#![no_std]
2#![deny(missing_docs)]
3
4//! Variadic function support for Rust.
5//!
6//! # Install
7//!
8//! ```console
9//! cargo add variadiz
10//! ```
11//!
12//! # Example
13//!
14//! ```
15//! use variadiz::*;
16//!
17//! #[variadic]
18//! fn print<T, U>(mut counter: usize, non_variadic: T, others: Option<U>)
19//! where
20//!     T: std::fmt::Display,
21//!     U: std::fmt::Debug,
22//! {
23//!     #[va_expand_ref(mut counter: usize)]
24//!     {
25//!         println!("{counter}: {:?}", others);
26//!         *counter += 1;
27//!     }
28//!     #[va_expand_mut]
29//!     {
30//!         others.take();
31//!     }
32//!     #[va_expand(mut counter: usize, non_variadic: T)]
33//!     {
34//!         println!("[{non_variadic}] {counter}: {:?}", others);
35//!         *counter += 1;
36//!     }
37//! }
38//!
39//! print(
40//!     0,
41//!     20240429,
42//!     va_args!(Some("hello"), Some(vec![1, 2, 3]), Some('e')),
43//! );
44//! ```
45//!
46//! Outputs:
47//!
48//! ```text
49//! 0: Some("hello")
50//! 1: Some([1, 2, 3])
51//! 2: Some('e')
52//! [20240429] 3: None
53//! [20240429] 4: None
54//! [20240429] 5: None
55//! ```
56//!
57//! As methods:
58//!
59//! ```
60//! use std::fmt::Debug;
61//! use variadiz::*;
62//!
63//! struct Person<'a, T>
64//! where
65//!     T: Debug,
66//! {
67//!     name: &'a str,
68//!     age: u32,
69//!     tags: Vec<T>,
70//! }
71//!
72//! #[variadic_impl]
73//! impl<'a, T> Person<'a, T>
74//! where
75//!     T: Debug,
76//! {
77//!     // Non-variadic method
78//!     fn hello(&self) -> &'static str {
79//!         "hello"
80//!     }
81//!
82//!     #[variadic]
83//!     fn grow_up<U>(&mut self, others: U)
84//!     where
85//!         U: std::fmt::Debug,
86//!     {
87//!         #[va_expand(hello: &str, who: &str, mut age: u32, tags: Vec<T>)]
88//!         #[va_bind(hello = self.hello())]
89//!         #[va_bind(who = self.name)]
90//!         #[va_bind(age = self.age)]
91//!         #[va_bind(tags = self.tags)]
92//!         {
93//!             println!("{hello}, {who} is {age} years old,");
94//!             println!("\tthey are {tags:?},");
95//!             println!("\tand do not forget {others:?}");
96//!             *self.age += 1;
97//!         }
98//!     }
99//! }
100//!
101//! let mut person = Person {
102//!     name: "John",
103//!     age: 16,
104//!     tags: vec!["smart", "awesome"],
105//! };
106//! person.grow_up(va_args!("hell", Some(62), 0.96));
107//! ```
108//!
109//! Outputs:
110//!
111//! ```text
112//! hello, John is 16 years old,
113//!         they are ["smart", "awesome"],
114//!         and do not forget "hell"
115//! hello, John is 17 years old,
116//!         they are ["smart", "awesome"],
117//!         and do not forget Some(62)
118//! hello, John is 18 years old,
119//!         they are ["smart", "awesome"],
120//!         and do not forget 0.96
121//! ```
122//!
123//! # Details
124//!
125//! ## The [`#[variadic]`](crate::variadic) attribute
126//!
127//! This attribute macro always takes the last declared generic type and the last parameter for variadic, i.e.:
128//!
129//! ```no_run
130//! # use variadiz::*;
131//! // The generic type `U` and the parameter `others` are used for variadic.
132//! #[variadic]
133//! fn print<T, U>(counter: usize, non_variadic: T, others: Option<U>) {
134//!     todo!()
135//! }
136//! ```
137//!
138//! **NOTE**: It is undefined behavior to use the variadic generic type elsewhere,
139//! including be used in bounds of other generic types.
140//!
141//! However, conversely the variadic generic type can be bound by other generic types:
142//!
143//! ```no_run
144//! # use variadiz::*;
145//! #[variadic]
146//! fn print<T, U>(counter: usize, non_variadic: T, others: Option<U>)
147//! where
148//!     // T: From<U>,  // Bad, the behavior is undefined
149//!     U: From<T>,     // OK
150//! {
151//!     todo!()
152//! }
153//! ```
154//!
155//! ## Expand variadic parameter pack
156//!
157//! To expand the variadic parameter pack, you need to add a `#[va_expand]` attribute on a block:
158//!
159//! ```no_run
160//! # use variadiz::*;
161//! # #[variadic]
162//! # fn print<T, U>(counter: usize, non_variadic: T, others: Option<U>) {
163//! #[va_expand]
164//! {
165//!     todo!()
166//! }
167//! # }
168//! ```
169//!
170//! Anyway, `#[va_expand]` always consumes all variadic parameters, even if them are bound by `Copy`.
171//!
172//! Instead, using `#[va_expand_ref]` and `#[va_expand_mut]`, you will get an immutable reference or
173//! a mutable reference to each variadic parameter, allowing you to expand the variadic parameter pack
174//! multiple times.
175//!
176//! It should be noted that the expansion block behaves like a function body rather than a closure body -
177//! it cannot capture variables from the context automatically.
178//!
179//! To capture context variables, you must declare them like how you declare function parameters:
180//!
181//! ```no_run
182//! # use variadiz::*;
183//! #[variadic]
184//! fn print<T>(x: i32, y: &str, others: T) {
185//!     #[va_expand(x: i32, y: &str)]
186//!     {
187//!         todo!()
188//!     }
189//! }
190//! ```
191//!
192//! **NOTE**: Since the captured variables must be usable multiple times after being expanded,
193//! we can only use their references anyway.
194//! So when you declare to capture `x`, the `&x` is actually captured.
195//! You can add a `mut` before the captured variable to indicate capturing its mutable reference.
196//!
197//! For example:
198//!
199//! ```
200//! use variadiz::*;
201//!
202//! #[variadic]
203//! fn print<T, U>(mut counter: usize, non_variadic: T, others: Option<U>)
204//! where
205//!     T: std::fmt::Display,
206//!     U: std::fmt::Debug,
207//! {
208//!     // Capture `counter` by mutable reference.
209//!     #[va_expand_ref(mut counter: usize)]
210//!     {
211//!         println!("{counter}: {:?}", others);
212//!         // `counter` here is actually `&mut usize`,
213//!         //  a mutable reference to the original `counter`.
214//!         *counter += 1;
215//!     }
216//!     #[va_expand_mut]
217//!     {
218//!         others.take();
219//!     }
220//!     // Capture `counter` by mutable reference,
221//!     // then capture `non_variadic` by immutable reference.
222//!     #[va_expand(mut counter: usize, non_variadic: T)]
223//!     {
224//!         println!("[{non_variadic}] {counter}: {:?}", others);
225//!         *counter += 1;
226//!     }
227//! }
228//!
229//! print(
230//!     0,
231//!     20240429,
232//!     va_args!(Some("hello"), Some(vec![1, 2, 3]), Some('e')),
233//! );
234//! ```
235//!
236//! Another example:
237//!
238//! ```rust
239//! use variadiz::*;
240//!
241//! #[variadic]
242//! fn collect<T, U>(mut collector: Vec<T>, others: Option<U>) -> Vec<T>
243//! where
244//!     U: Into<T>, // `U` can be bound by `T`, but not vice versa.
245//! {
246//!     // `collector` is actually `&mut Vec<T>`
247//!     #[va_expand(mut collector: Vec<T>)]
248//!     {
249//!         if let Some(item) = others {
250//!             // The type `U` is specific to each variadic parameter.
251//!             // `U` outside an expanded block is **undefined**.
252//!             collector.push(<U as Into<T>>::into(item));
253//!         }
254//!     }
255//!     collector
256//! }
257//!
258//! let strs = collect(
259//!     vec![String::from("hello")],
260//!     va_args!(Some("world"), None::<std::borrow::Cow<str>>, Some('e')),
261//! );
262//! println!("{:?}", strs);
263//! ```
264//!
265//! Outputs:
266//!
267//! ```text
268//! ["hello", "world", "e"]
269//! ```
270//!
271//! You can use an expanded block almost anywhere expressions can be used,
272//! and it always evaluates to a `()`.
273//! However, there are two places where you cannot use a expanded block:
274//! in another expanded block, or in a macro.
275//! For examples:
276//!
277//! ```
278//! use variadiz::*;
279//!
280//! #[variadic]
281//! fn print<T>(mut counter: usize, others: Option<T>)
282//! where
283//!     T: std::fmt::Debug,
284//! {
285//!     let _result = (0..10)
286//!         .map(|i| {
287//!             if i % 2 == 0 {
288//!                 #[va_expand_ref(mut counter: usize)]
289//!                 {
290//!                     println!("{counter}: {:?}", others);
291//!                     *counter += 1;
292//!                 }
293//!                 counter
294//!             } else {
295//!                 #[va_expand_ref(mut counter: usize)]
296//!                 {
297//!                     println!("{counter}: {:?}", others);
298//!                     *counter -= 1;
299//!                 }
300//!                 10 - counter
301//!             }
302//!         })
303//!         .collect::<Vec<_>>();
304//!
305//!     // Expanded block in an expanded block is not allowed.
306//!     // #[va_expand_ref(mut counter: usize)]
307//!     // {
308//!     //     #[va_expand_ref(mut counter: usize)]
309//!     //     {
310//!     //         println!("{counter}: {:?}", others);
311//!     //         *counter += 1;
312//!     //     }
313//!     // }
314//!
315//!     // Expanded block in a macro is not allowed.
316//!     // call_macro! {
317//!     //     #[va_expand_ref(mut counter: usize)]
318//!     //     {
319//!     //         println!("{counter}: {:?}", others);
320//!     //         *counter += 1;
321//!     //     }
322//!     //};
323//! }
324//!
325//! print(0, va_args!(Some("hello"), Some(vec![1, 2, 3]), Some('e')));
326//! ```
327//!
328//! Except for the captured variables, all generic types, and the identifier of the variadic parameter pack,
329//! the expanded block cannot interact with the outer code block.
330//!
331//! This means, you cannot return a value to the outer by omitting the semicolon of the last statement,
332//! nor can you operate on the outer's control flow (i.e., `for`, `loop`, labelled block) via `continue`
333//! and `break`.
334//!
335//! A statement `return` in an expanded block will only exit the expanded block itself (similar to `continue`
336//! in a `loop` block) rather than exiting the outer function.
337//!
338//! ## Bind captured variables
339//!
340//! You can use the `#[va_bind]` attribute to bind a captured variable to another value, for example:
341//!
342//! ```
343//! use variadiz::*;
344//!
345//! struct Person {
346//!     name: String,
347//!     age: u32,
348//! }
349//!
350//! #[variadic]
351//! fn print<T>(mut person: Person, interests: T)
352//! where
353//!     T: std::fmt::Debug,
354//! {
355//!     #[va_expand_ref(who: String, mut age: u32)]
356//!     #[va_bind(who = person.name, age = person.age)]
357//!     {
358//!         println!("{who} is {age} years old");
359//!         println!("And they are interested in {interests:?}");
360//!         println!("then a year passed...");
361//!         *age += 1;
362//!     }
363//!
364//!     /// Although you cannot split the `va_expand` attribute,
365//!     /// but you can split the `va_bind` attribute.
366//!     /// The following code is totally equivalent:
367//!     #[va_expand_ref(who: String, mut age: u32)]
368//!     #[va_bind(who = person.name)]
369//!     #[va_bind(age = person.age)]
370//!     {
371//!         println!("{who} is {age} years old");
372//!         println!("And they are interested in {interests:?}");
373//!         println!("then a year passed...");
374//!         *age += 1;
375//!     }
376//! }
377//!
378//! let person = Person {
379//!     name: "John".to_string(),
380//!     age: 18,
381//! };
382//! print(person, va_args!("math", (), 114514));
383//! ```
384//!
385//! **NOTE**: Binding a value to a captured variable does **NOT** move it.
386//!
387//! However, you need to be careful about traps in the case of binding variables
388//! to r-values (called value expressions in Rust):
389//!
390//! ```
391//! use variadiz::*;
392//!
393//! #[variadic]
394//! fn count<T>(_others: T) {
395//!     let mut counter = 0;
396//!
397//!     // Bind to l-value (called place expression in Rust).
398//!     #[va_expand_ref(mut counter: usize)]
399//!     #[va_bind(counter = counter)] // Default behavior.
400//!     {
401//!         *counter += 1;
402//!     }
403//!     // `counter` is updated.
404//!     assert_eq!(counter, 4);
405//!
406//!     counter = 0;
407//!
408//!     // Bind to r-value (called value expression in Rust).
409//!     #[va_expand_ref(mut counter: usize)]
410//!     #[va_bind(counter = counter + 1 - 1)]
411//!     {
412//!         *counter += 1;
413//!     }
414//!     // `counter` is NOT updated!
415//!     assert_eq!(counter, 0);
416//! }
417//!
418//! count(va_args!(1, "2", 3.0, [4]));
419//! ```
420//!
421//! ## Call variadic function
422//!
423//! It is easy to see from the above example that you should pack all variadic arguments into [`va_args!`] macro
424//! and pass them as a single argument.
425//!
426//! The [`va_args!`] macro accepts any number of expressions. Sometimes you may want to annotate the type
427//! of each argument, you can use the [`va_types!`] macro:
428//!
429//! ```
430//! # use variadiz::*;
431//! #
432//! # #[variadic]
433//! # fn print<T, U>(mut counter: usize, non_variadic: T, others: Option<U>)
434//! # where
435//! #     T: std::fmt::Display,
436//! #     U: std::fmt::Debug,
437//! # {
438//! #     #[va_expand_ref(mut counter: usize)]
439//! #     {
440//! #         println!("{counter}: {:?}", others);
441//! #         *counter += 1;
442//! #     }
443//! #     #[va_expand_mut]
444//! #     {
445//! #         others.take();
446//! #     }
447//! #     #[va_expand(mut counter: usize, non_variadic: T)]
448//! #     {
449//! #         println!("[{non_variadic}] {counter}: {:?}", others);
450//! #         *counter += 1;
451//! #     }
452//! # }
453//! #
454//! let args: va_types!(Option<&str>, Option<Vec<usize>>, Option<char>) =
455//!     va_args!(Some("hello"), Some(vec![1, 2, 3]), Some('e'));
456//! print(0, 20240429, args);
457//! ```
458//!
459//! ## Variadic methods support
460//!
461//! Due to some implementation details, [`#[variadic]`](variadic) has to define some
462//! `trait` item outside the variadic function. It conflicts with `impl` item --
463//! we cannot define `trait` item in a `impl` item.
464//!
465//! To solve this problem, you are required to add the [`#[variadic_impl]`](variadic_impl)
466//! attribute on the `impl` item to assist moving these `trait` items out.
467//!
468//! For example:
469//!
470//! ```
471//! use std::fmt::Debug;
472//! use variadiz::*;
473//!
474//! struct Person<'a, T>
475//! where
476//!     T: Debug,
477//! {
478//!     name: &'a str,
479//!     age: u32,
480//!     tags: Vec<T>,
481//! }
482//!
483//! #[variadic_impl]
484//! impl<'a, T> Person<'a, T>
485//! where
486//!     T: Debug,
487//! {
488//!     // Non-variadic method
489//!     fn hello(&self) -> &'static str {
490//!         "hello"
491//!     }
492//!
493//!     #[variadic]
494//!     fn grow_up<U>(&mut self, others: U)
495//!     where
496//!         U: std::fmt::Debug,
497//!     {
498//!         #[va_expand(hello: &str, who: &str, mut age: u32, tags: Vec<T>)]
499//!         #[va_bind(hello = self.hello())]
500//!         #[va_bind(who = self.name)]
501//!         #[va_bind(age = self.age)]
502//!         #[va_bind(tags = self.tags)]
503//!         {
504//!             println!("{hello}, {who} is {age} years old,");
505//!             println!("\tthey are {tags:?},");
506//!             println!("\tand do not forget {others:?}");
507//!             *self.age += 1;
508//!         }
509//!     }
510//! }
511//!
512//! let mut person = Person {
513//!     name: "John",
514//!     age: 16,
515//!     tags: vec!["smart", "awesome"],
516//! };
517//! person.grow_up(va_args!("hell", Some(514), 0.96));
518//! ```
519//!
520//! **NOTE**: The `#[variadic]` attribute in the [`#[variadic_impl]`](variadic_impl) item is
521//! not really the attribute macro: it is handled directly and will be removed by
522//! [`#[variadic_impl]`](variadic_impl).
523//! Retaining its name as `#[variadic]` is an ergonomic consideration.
524//! This means **it is not affected by Rust's symbol resolution**.
525//!
526//! ## Variadic trait methods support?
527//!
528//! To support variadic methods in traits, it requires sharing private bounds between the trait
529//! definition and each implementations.
530//! There is current no good design for this purpose.
531
532#[doc(hidden)]
533pub use tuplez as __tuplez;
534
535/// Create a variadic argument pack.
536///
537/// Accept many expressions separated by commas, and the result of each expression will
538/// be treated as a argument.
539///
540/// As an extension, it is allowed to use a semicolon plus a number literal to indicate repetition,
541/// e.g.:
542///
543/// ```
544/// use variadiz::va_args;
545///
546/// let repetition = va_args!(3.14, "hello";3, Some(5));
547/// let full = va_args!(3.14, "hello", "hello", "hello", Some(5));
548/// assert_eq!(repetition, full);
549/// ```
550pub use tuplez::tuple as va_args;
551
552/// Annotate types for variadic arguments.
553///
554/// Accept many types separated by commas.
555///
556/// As an extension, it is allowed to use a semicolon plus a number literal to indicate repetition,
557/// e.g.:
558///
559/// ```
560/// use variadiz::{va_args, va_types};
561///
562/// let repetition: va_types!(f32, &str, &str, &str, Option<i32>) =
563///     va_args!(3.14, "hello";3, Some(5));
564/// let full: va_types!(f32, &str;3, Option<i32>) =
565///     va_args!(3.14, "hello", "hello", "hello", Some(5));
566/// assert_eq!(repetition, full);
567/// ```
568pub use tuplez::tuple_t as va_types;
569
570/// Define a variadic function.
571///
572/// Please check the [documentation home page](crate) for details.
573pub use variadiz_macros::variadic;
574
575/// Indicates that there are variadic methods in the `impl` item.
576///
577/// Please check the [variadic methods support section](crate#variadic-methods-support) for details.
578pub use variadiz_macros::variadic_impl;
579
580extern crate self as variadiz;