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;