tuplez/
lib.rs

1#![cfg_attr(feature = "any_array", allow(incomplete_features))]
2#![cfg_attr(feature = "any_array", feature(generic_const_exprs))]
3#![cfg_attr(feature = "any_array", feature(maybe_uninit_array_assume_init))]
4#![cfg_attr(feature = "any_array", feature(maybe_uninit_uninit_array_transpose))]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![deny(missing_docs)]
7#![cfg_attr(not(feature = "std"), no_std)]
8
9//! Tuples represented in recursive form rather than parallel form.
10//!
11//! # Motivation
12//!
13//! The [primitive tuple types](https://doc.rust-lang.org/std/primitive.tuple.html) are represented in parallel form, like:
14//!
15//! ```text
16//! (a, b, c, d ...)
17//! ```
18//!
19//! Since Rust doesn't support variadic generics, we cannot add methods to primitive tuples with any number of elements.
20//!
21//! Currently, most tuple crates use declarative macros to implement methods for tuples whose number of elements is less than
22//! a certain limit (usually 32).
23//!
24//! To solve this, tuplez introduces a [`Tuple`] type represented in recursive form, like:
25//!
26//! ```text
27//! Tuple(a, Tuple(b, Tuple(c, Tuple(d, ...))))
28//! ```
29//!
30//! The advantage of this representation is that
31//! [you can implement methods recursively for all tuples](Tuple#trait-implementations-on-tuple),
32//! and there is no longer a limit on the number of tuple's elements. And, in almost all cases,
33//! the [`Tuple`] takes no more memory than the primitive tuples.
34//!
35//! # Functionality
36//!
37//! * [Create tuples](tuple!) with any number of elements.
38//! * [Access elements](get!) in a tuple at any index, or by their types.
39//! * [Push element](TupleLike::push()) to a tuple or [pop element](TupleLike::pop()) from a tuple.
40//! * [Join](TupleLike::join()) two tuples or [split](split_at!) a tuple into two parts.
41//! * [Rich tuple operations](TupleLike), e.g.: [reverse](TupleLike::rev()),
42//!   [left rotate](TupleLike::rot_l()), [zip](TupleLike::zip()).
43//! * [Get subsequences](Tuple#get-subsequences).
44//! * If all element types implement a `Trait` (e.g. `Eq`, `Add`), then the [`Tuple`] also implement that `Trait`.
45//!   [See which traits are supported and learn how to implement your custom traits for `Tuple`](Tuple#trait-implementations-on-tuple).
46//! * [Traverse all elements](Tuple#traverse-tuples) of a tuple, or [fold](Tuple#fold-tuples) a tuple.
47//! * When the number of elements of a tuple doesn't exceed 32, then it can be converted from/to
48//!   [a primitive tuple](Tuple#convert-fromto-primitive-tuples)
49//!   or [a primitive array](Tuple#convert-fromto-primitive-arrays).
50//!
51//! # Optional features
52//!
53//! * `unwrap` : (*by default*) Allows converting a tuple whose elements are all wrappers
54//!   into a tuple of the values those wrappers contain.
55//! * `uninit`: Add APIs for tuples consisting of [`MaybeUninit`](std::mem::MaybeUninit) elements.
56//! * `serde`: Derive `Serialize` and `Deserialize` for tuples.
57//! * `std`: (*by default*) Use standard library. It also enables `std` feature of [serde](https://crates.io/crates/serde).
58//! * `alloc`: Use standard `alloc` library. It also enables `alloc` feature of [serde](https://crates.io/crates/serde).
59//!   This feature is usually used when `std` is disabled.
60//! * `any_array`: (*nightly*) Use Rust's unstable feature to implement conversion
61//!   from/to primitive arrays on tuples with any number of elements.
62//!   This feature requires compiling with rustc released to nightly channel.
63//!
64//! Bundles:
65//!
66//! * `default`: Enable default features, which are: `std`, `unwrap`.
67//! * `full-no-std`: Enable all features available on stable Rust without `std`, which are: `serde`, `uninit` and `unwrap`.
68//!   Note that `default-features = false` is necessary.
69//! * `full`: Enable all features available on stable Rust, which are: `serde`, `std`, `uninit` and `unwrap`.
70//! * `full-nightly`: Enable all features (requires nightly Rust).
71//!
72//! # Examples
73//!
74//! ```
75//! # #![cfg_attr(feature = "any_array", feature(generic_const_exprs))]
76//! #
77//! use tuplez::*;
78//!
79//! let tup = tuple!(1, "hello".to_string(), 3.14);
80//! let tup2 = Tuple::from((2, " world", -3.14));
81//! let tup3 = tup + tup2;
82//! assert_eq!(tup3, tuple!(3, "hello world".to_string(), 0.0));
83//!
84//! let tup4 = tup3.push(Some([1, 2, 3]));
85//! let (tup5, popped) = tup4.pop_front();
86//! assert_eq!(
87//!     tup5,
88//!     tuple!("hello world".to_string(), 0.0, Some([1, 2, 3]))
89//! );
90//! assert_eq!(popped, 3);
91//!
92//! let tup6 = tup5.rev();
93//! assert_eq!(
94//!     tup6,
95//!     tuple!(Some([1, 2, 3]), 0.0, "hello world".to_string())
96//! );
97//! let tup7 = tup6.rot_l();
98//! assert_eq!(
99//!     tup7,
100//!     tuple!(0.0, "hello world".to_string(), Some([1, 2, 3]))
101//! );
102//!
103//! let tup8 = tup7.foreach(mapper! {
104//!     |x: f64| -> String { x.to_string() }
105//!     |x: Option<[i32; 3]>| -> String { format!("{:?}", x.unwrap()) }
106//!     |x: String| { x }
107//! });
108//! let arr = tup8.to_array();
109//! assert_eq!(
110//!     arr,
111//!     [
112//!         "0".to_string(),
113//!         "hello world".to_string(),
114//!         "[1, 2, 3]".to_string()
115//!     ]
116//! );
117//!
118//! let tup9 = tuple!(1, "2", 3.0);
119//! let result = tup9.fold(
120//!     tuple!(
121//!         |acc, x| (acc + x) as f64,
122//!         |acc: f64, x: &str| acc.to_string() + x,
123//!         |acc: String, x| acc.parse::<i32>().unwrap() + x as i32,
124//!     ),
125//!     0,
126//! );
127//! assert_eq!(result, 15);
128//! ```
129//!
130//! Please check [`Tuple`]'s documentation page for detailed usage.
131
132extern crate self as tuplez;
133
134#[cfg(not(feature = "std"))]
135extern crate core as std;
136
137#[cfg(all(not(feature = "std"), feature = "alloc"))]
138extern crate alloc;
139
140pub mod fold;
141pub mod foreach;
142mod macros;
143pub mod ops;
144pub mod predicate;
145pub mod search;
146mod tuple;
147mod tupleize;
148
149#[cfg(feature = "uninit")]
150#[cfg_attr(docsrs, doc(cfg(feature = "uninit")))]
151pub mod uninit;
152
153#[cfg(feature = "any_array")]
154mod any_array;
155
156#[cfg(feature = "unwrap")]
157#[cfg_attr(docsrs, doc(cfg(feature = "unwrap")))]
158pub mod unwrap;
159
160pub use tuple::*;
161
162pub use tupleize::Tupleize;
163
164// Used for re-exporting.
165#[doc(hidden)]
166pub use tuplez_macros::{
167    folder as folder_inner, mapper as mapper_inner, split_at as split_at_inner, take as take_inner,
168    tuple as tuple_inner, tuple_pat as tuple_pat_inner, tuple_t as tuple_t_inner,
169    unary_pred as unary_pred_inner,
170};
171
172/// Get the element at a specific index of the tuple.
173///
174/// The [`get_ref()`](TupleLike::get_ref()) and [`get_mut()`](TupleLike::get_mut())
175/// provide another way to get elements by their type.
176///
177/// # Syntax
178///
179/// `get!(Expr; Index)`
180///
181/// **The index must be an integer literal** since procedural macros do not yet support evaluating constants.
182///
183/// # Explanation
184///
185/// This macro will be expanded to standard member access syntax:
186///
187/// ```text
188/// get!(tup; 0) => tup.0
189/// get!(tup; 1) => tup.1.0
190/// get!(tup; 2) => tup.1.1.0
191/// ```
192///
193/// Expressions are automatically quoted, so don't worry:
194///
195/// ```text
196/// get!(tup1 + tup2; 3) => (tup1 + tup2).1.1.1.0
197/// ```
198///
199/// You can use `&` and `&mut` directly on the output of [`get!`], like:
200///
201/// ```
202/// use tuplez::{get, tuple};
203///
204/// fn add(x: &i32, y: &i32) -> i32 { x + y }
205///
206/// fn add_one(x: &mut i32) { *x += 1; }
207///
208/// let mut tup = tuple!(1, "hello", 3.14);
209///
210/// let x = add(&get!(tup; 0), &2);             // Immutably reference the first element of `tup`
211/// assert_eq!(tup, tuple!(1, "hello", 3.14));  // Then `tup` remains unchanged
212/// assert_eq!(x, 3);
213///
214/// add_one(&mut get!(tup; 0));                 // Mutably reference the first element of `tup`
215/// assert_eq!(tup, tuple!(2, "hello", 3.14));  // Then `tup` changes
216///
217/// get!(tup; 1) = "world";                     // Direct access the second element of `tup`
218/// assert_eq!(tup, tuple!(2, "world", 3.14));
219/// ```
220///
221/// It's not a problem for nested tuples either:
222///
223/// ```
224/// use tuplez::{get, tuple};
225///
226/// fn push_world(s: &mut String) {
227///     s.push_str(" world");
228/// }
229///
230/// let mut tup = tuple!(1, tuple!("hello".to_string(), 3.14));
231/// push_world(&mut get!(get!(tup; 1); 0));
232/// assert_eq!(tup, tuple!(1, tuple!("hello world".to_string(), 3.14)));
233/// ```
234pub use tuplez_macros::get;
235
236/// Pick some elements from a tuple.
237///
238/// # Syntax
239///
240/// ```text
241/// IndicesGroup      = Index [ - Index ]
242///
243/// pick!(Expr; IndicesGroup1 [, IndicesGroup2, ...] )
244/// ```
245///
246/// *`[` and `]` only indicate the optional content but not that they need to be input.*
247///
248/// *Similarly, `...` indicates several repeated segments, rather than inputting `...`.*
249///
250/// **The index must be an integer literal** since procedural macros do not yet support evaluating constants.
251///
252/// # Explanation
253///
254/// The `pick!` macro allows you to pick some elements you want from a tuple to a new tuple,
255/// and the unpicked elements will be put into a new tuple.
256///
257/// ```
258/// use tuplez::{pick, tuple};
259///
260/// let tup = tuple!(1, "hello", 3.14, [1, 2, 3], Some(9.8), 'c');
261/// let (picked, unpicked) = pick!(tup; 3, 1, 5);
262/// assert_eq!(picked, tuple!([1, 2, 3], "hello", 'c'));
263/// assert_eq!(unpicked, tuple!(1, 3.14, Some(9.8)));
264/// ```
265///
266/// You can abbreviate the continuous part as `start - end`:
267///
268/// ```
269/// use tuplez::{pick, tuple};
270///
271/// let tup = tuple!(1, "hello", 3.14, [1, 2, 3], Some(9.8), 'c');
272/// let (picked, unpicked) = pick!(tup; 4, 1-3);
273/// assert_eq!(picked, tuple!(Some(9.8), "hello", 3.14, [1, 2, 3]));
274/// assert_eq!(unpicked, tuple!(1, 'c'));
275/// ```
276///
277/// Of course, reverse ranges are also supported:
278///
279/// ```
280/// use tuplez::{pick, tuple};
281///
282/// let tup = tuple!(1, "hello", 3.14, [1, 2, 3], Some(9.8), 'c');
283/// let (picked, unpicked) = pick!(tup; 4, 3-1);    // `3-1` is reverse range
284/// assert_eq!(picked, tuple!(Some(9.8), [1, 2, 3], 3.14, "hello"));
285/// assert_eq!(unpicked, tuple!(1, 'c'));
286/// ```
287///
288/// If Rust's move checker allows it, then you can pick the same element multiple times:
289///
290/// ```
291/// use tuplez::{pick, tuple};
292///
293/// let tup = tuple!(1, "hello", 3.14, [1, 2, 3], Some(9.8), 'c');
294/// let (picked, unpicked) = pick!(tup; 1, 1, 1, 5, 5);
295/// assert_eq!(picked, tuple!("hello", "hello", "hello", 'c', 'c'));
296/// assert_eq!(unpicked, tuple!(1, 3.14, [1, 2, 3], Some(9.8)));
297/// ```
298///
299/// Another common use is when you want to pick references to some elements of the original tuple:
300///
301/// ```
302/// use tuplez::{get, pick, tuple, TupleLike};
303///
304/// let mut tup = tuple!(1, "hello", 3.14, [1, 2, 3], Some(9.8), 'c');
305/// let (picked, _) = pick!(tup.as_mut(); 3, 0);
306/// get!(picked; 0).rotate_left(1);
307/// *get!(picked; 1) += 100;
308/// assert_eq!(tup, tuple!(101, "hello", 3.14, [2, 3, 1], Some(9.8), 'c'));
309/// ```
310///
311/// NOTE: Unlike [`take!`], even if you [`pick!`] only one element, you still get a tuple.
312pub use tuplez_macros::pick;
313
314/// Swap two parts of a tuple.
315///
316/// # Syntax
317///
318/// ```text
319/// IndicesGroup      = Index [ - Index ]
320///
321/// swap_at!(Expr; IndicesGroup1 , IndicesGroup2 )
322/// ```
323///
324/// *`[` and `]` only indicate the optional content but not that they need to be input.*
325///
326/// **The index must be an integer literal** since procedural macros do not yet support evaluating constants.
327///
328/// # Explanation
329///
330/// You can swap two elements of a tuple, then generating a new tuple:
331///
332/// ```
333/// use tuplez::{swap_at, tuple};
334///
335/// let tup = tuple!(1, "2", 3.14, [1, 2, 3], Some(9.8), 'c', 14);
336/// let tup2 = swap_at!(tup; 0, 4);
337/// assert_eq!(tup2, tuple!(Some(9.8), "2", 3.14, [1, 2, 3], 1, 'c', 14));
338/// ```
339///
340/// You can also specify a continuous range of elements via `start - end`,
341/// but the number of elements on both sides must be equal:
342///
343/// ```
344/// use tuplez::{swap_at, tuple};
345///
346/// let tup = tuple!(1, "2", 3.14, [1, 2, 3], Some(9.8), 'c', 14);
347/// let tup2 = swap_at!(tup; 0-2, 3-5);
348/// assert_eq!(tup2, tuple!([1, 2, 3], Some(9.8), 'c', 1, "2", 3.14, 14));
349/// ```
350///
351/// Of course, reverse ranges are also supported:
352///
353/// ```
354/// use tuplez::{swap_at, tuple};
355///
356/// let tup = tuple!(1, "2", 3.14, [1, 2, 3], Some(9.8), 'c', 14);
357/// let tup2 = swap_at!(tup; 0-2, 5-3);
358/// assert_eq!(tup2, tuple!('c', Some(9.8), [1, 2, 3], 3.14, "2", 1, 14));
359/// ```
360pub use tuplez_macros::swap_at;
361
362/// Pass the elements of a tuple as arguments to a function or method.
363///
364/// # Syntax
365///
366/// ```text
367/// ArgsGroup   = [ & [mut] ] Index [ - Index ]
368///
369/// apply!( Expr => Func ( [ArgsGroup1, ArgsGroup2, ...] ) )
370/// ```
371///
372/// *`[` and `]` only indicate the optional content but not that they need to be input.*
373///
374/// *Similarly, `...` indicates several repeated segments, rather than inputting `...`.*
375///
376/// **The index must be an integer literal** since procedural macros do not yet support evaluating constants.
377///
378/// # Explanation
379///
380/// You can pass the elements of the tuple into the function or method in the order you want:
381///
382/// ```
383/// use tuplez::{tuple, apply};
384///
385/// fn test(_: i32, _: f64, _: &str) {}
386///
387/// let tup = tuple!(3.4, 2, "hello", [1, 2, 3], 8);
388/// apply!(tup => test(4, 0, 2));   // Expand to `test(8, 3.4, "hello")`
389/// ```
390///
391/// Parts in the same order can be omitted in the form `start - end`:
392///
393/// ```
394/// use tuplez::{tuple, apply};
395///
396/// fn test(_: i32, _: f64, _: &str, _: [i32; 3]) {}
397///
398/// let tup = tuple!([1, 2, 3], 2, 3.4, "hello", 9);
399/// apply!(tup => test(1-3, 0));    // `1-3` expands to `1, 2, 3`
400/// ```
401///
402/// And reverse ranges are also supported:
403///
404/// ```
405/// use tuplez::{tuple, apply};
406///
407/// fn test(_: &str, _: f64, _: i32, _: [i32; 3]) {}
408///
409/// let tup = tuple!([1, 2, 3], 2, 3.4, "hello", 9);
410/// apply!(tup => test(3-1, 0));    // `3-1` expands to `3, 2, 1`
411/// ```
412///
413/// You can add `&` or `&mut` to elements to borrow them. Here is a slightly more complex example:
414///
415/// ```
416/// use tuplez::{tuple, apply};
417///
418/// struct DoIt<T>(T);
419/// impl<T> DoIt<T> {
420///     fn do_sth(&self, _: String, _: f64, _: &str, _: &mut i32, _: &mut [T; 3], _: &i32) {}
421/// }
422///
423/// let tup = tuple!(
424///     1,
425///     [5, 2, 4],
426///     9.8,
427///     "world".to_string(),
428///     3.14,
429///     "hello",
430///     7,
431///     "zzz"
432/// );
433/// apply!(tup => DoIt::<i32>(7).do_sth(3-5, &mut 0-1, &6));
434/// ```
435///
436/// NOTE: [`apply!`] consumes the tuple, even if you add `&` or `&mut` to each elements.
437/// Sometimes you can avoid this by adding a `&` or `&mut` before the tuple:
438///
439/// ```
440/// use tuplez::{tuple, apply};
441///
442/// fn push(v: &mut Vec<i32>, x: i32) {
443///     v.push(x)
444/// }
445///
446/// let mut tup = tuple!(vec![1, 2], 3);
447/// apply!(&mut tup => push(&mut 0, 1));
448/// assert_eq!(tup, tuple!(vec![1, 2, 3], 3));
449/// ```
450///
451/// It is worth mentioning that the input tuple can be any expression.
452/// The `&tup` and the `&mut tup` are just two of the many possible inputs.
453///
454/// You can use the same element multiple times as long as Rust's move checker and borrow checker allow it:
455///
456/// ```
457/// use tuplez::{tuple, apply};
458///
459/// fn test(_: i32, _: i32, _: i32, _: &i32, _: &i32, _: &mut i32) {}
460///
461/// let tup = tuple!(1, 2, 3);
462/// apply!(tup => test(0-2, &1-2, &mut 0));
463/// ```
464pub use tuplez_macros::apply;
465
466/// Derive [`Tupleize`] on struct.
467pub use tuplez_macros::Tupleize;