tuple_conv/lib.rs
1//! A simple crate for providing conversions from tuples to vectors and boxed
2//! slices.
3//!
4//! # Small example
5//!
6//! This crate is pretty simple. Here's a trivial example.
7//!
8//! ```rust
9//! # extern crate tuple_conv;
10//! # use tuple_conv::RepeatedTuple;
11//! let t = (0, 1, 2);
12//! let v = t.to_vec();
13//! assert_eq!(v, [0, 1, 2]);
14//! ```
15//!
16//! # Motivation
17//!
18//! The primary motivation for using these tools is for syntactic elegance. In
19//! APIs where a small, but variable number of arguments of a single type is
20//! wanted, it's standard to use a `Vec<T>`. This can become cumbersome for the
21//! user, however - particularly when we have nested types. See, for example:
22//! ```
23//! fn do_something_2d(a: Vec<Vec<i32>>) { /* ... */ }
24//!
25//! do_something_2d(vec![vec![1, 2, 3],
26//! vec![4, 5, 6],
27//! vec![7, 8, 9]]);
28//! ```
29//! Calling this function is somewhat cumbersome, and can be made cleaner with:
30//! ```
31//! # extern crate tuple_conv;
32//! # use tuple_conv::RepeatedTuple;
33//! fn do_something_2d<T, S>(a: T) where
34//! T: RepeatedTuple<S>,
35//! S: RepeatedTuple<i32>,
36//! { /* ... */ }
37//!
38//! do_something_2d(((1, 2, 3),
39//! (4, 5, 6),
40//! (7, 8, 9)));
41//! ```
42//! Even though it starts to give us flashbacks from LISP, more of our code is
43//! made up of things we actually care about - gone with the `vec` macros
44//! everywhere. The primary benefit is simpler syntax.
45//!
46//! # Typical usage
47//!
48//! Although we can use [`RepeatedTuple`] as a type restriction, this would
49//! usually be replacing a `Vec`, so there's a good chance we'd still like to
50//! allow it. The main usage for this crate is then with the [`TupleOrVec`]
51//! trait - it's implemented for all repeated tuples and for every `Vec`, which
52//! allows us to easily change the public-facing API to allow tuples without
53//! removing any functionality.
54//!
55//! Here's how we'd go about doing that, given a function `foo` that takes some
56//! `Vec`:
57//! ```
58//! fn foo(v: Vec<&str>) {
59//! /* do stuff */
60//! }
61//!
62//! // a typical way to call `foo`:
63//! foo(vec!["bar", "baz"]);
64//! ```
65//! The first step is to change the function signature to accept tuples:
66//! ```
67//! extern crate tuple_conv;
68//! use tuple_conv::TupleOrVec;
69//!
70//! // ...
71//!
72//! fn foo<V: TupleOrVec<&'static str>>(v: V) {
73//! /* do stuff */
74//! }
75//! ```
76//! Then, convert the argument
77//! ```
78//! # extern crate tuple_conv;
79//! # use tuple_conv::TupleOrVec;
80//! fn foo<V: TupleOrVec<&'static str>>(v: V) {
81//! let v = v.as_vec();
82//! /* do stuff */
83//! }
84//! ```
85//! And now we can call `foo` like this:
86//! ```
87//! # extern crate tuple_conv;
88//! # use tuple_conv::TupleOrVec;
89//! # fn foo<V: TupleOrVec<&'static str>>(v: V) {}
90//! foo(("bar", "baz"));
91//! foo(vec!["baz", "bar"]);
92//! ```
93//! It is, however, worth keeping in mind the implications of large generic
94//! functions implemented for many types. This is discussed in more detail
95//! [below](#performance).
96//!
97//! # Limitations and performance
98//!
99//! ### Limitations
100//!
101//! Because each new tuple is a distinct type, we can only implement for
102//! finitely many tuple lengths. We've chosen to go up to tuples with 64
103//! elements of the same type. If you find yourself needing more (although I
104//! suspect this will be unlikely), the source is **relatively** simple, and
105//! not too difficult to extend.
106//!
107//! Additionally, because of the Rust's visibility rules for public traits,
108//! there isn't a good way to ensure that certain traits aren't implemented by
109//! others - like [`TupleOrVec`] for example. That being said, the trait is
110//! defined such that it *shouldn't* matter.
111//!
112//! ### Performance
113//!
114//! The details of the implementation are such that vectors are constructed in
115//! reverse, and `Vec<_>.reverse()` called, due to a limitation of Rust's macro
116//! system.
117//!
118//! This is not very significant (only ~10% increase with tuples of length 64),
119//! but something worth considering for performance-critical code. For more
120//! detail, pull this repository on [GitHub](add-link.com) and run
121//! `cargo bench`.
122//!
123//! There are two other considerations: time to compile and final binary size.
124//! These should usually be very minor - hardly noticeable. However: if you
125//! are having issues, keep this in mind: While these both may increase for
126//! functions that are being implemented on many types, it may be possible to
127//! reduce them by using public functions simply as wrappers for your internals
128//! that only take vectors.
129//!
130//! [`RepeatedTuple`]: trait.RepeatedTuple.html
131//! [`TupleOrVec`]: trait.TupleOrVec.html
132
133/// A trait implemented on all tuples composed of a single type.[^1]
134///
135/// The available methods for this trait are what make up the standard way to
136/// use this crate. Importing `RepeatedTuple` allows these methods to be called
137/// directly on types you're working with. This trait can also be used loosely
138/// as a bound specifically for repeated tuples, though there's nothing
139/// stopping someone from implementing it on their own type.
140///
141/// A particularly nice use case of `RepeatedTuple` is ensuring a nice syntax
142/// for your API. Because this is already discussed in the
143/// [crate-level documentation], more examples will not be given here.
144///
145/// ### A few notes:
146///
147/// While this trait **can** be used as a trait bound, you may find it better
148/// to instead use [`TupleOrVec`], as it also encapsulates vectors.
149///
150/// Additionally, while it is true in practice, there is no blanket
151/// implementation of `TupleOrVec` for all `RepeatedTuple`, due to compiler
152/// constraints.
153///
154/// Finally: The typical use case does not recommend or require re-implementing
155/// this trait, but nothing will break if you do.
156///
157/// [^1]: Please note that this is only implemented for tuples up to size 64.
158/// If you need more than 64, please fork this crate or submit a pull
159/// request.
160///
161/// [crate-level documentation]: index.html
162/// [`TupleOrVec`]: trait.TupleOrVec.html
163pub trait RepeatedTuple<E>: Sized {
164 /// Converts a tuple to a boxed slice, with elements in reverse order
165 fn to_boxed_slice_reversed(self) -> Box<[E]>;
166
167 /// Converts a tuple to a boxed slice of its elements
168 fn to_boxed_slice(self) -> Box<[E]> {
169 let mut s = self.to_boxed_slice_reversed();
170 s.reverse();
171 s
172 }
173
174 /// Converts a tuple to a vector, with elements in reverse order
175 fn to_vec_reversed(self) -> Vec<E> {
176 self.to_boxed_slice_reversed().into_vec()
177 }
178
179 /// Converts a tuple to a vector of its elements
180 fn to_vec(self) -> Vec<E> {
181 self.to_boxed_slice().into_vec()
182 }
183}
184
185/// A trait implemented on repeated tuples and vectors.
186///
187/// This trait has already been covered in the [crate-level documentation], so
188/// its coverage here will be brief.
189///
190/// This trait is implemented for all [repeated tuples] and all vectors, and
191/// serves as a drop-in replacement for functions that take vectors as
192/// arguments (albeit with some conversion). Types that were `Vec<T>` should be
193/// converted to generic types that implement `TupleOrVec<T>`.
194///
195/// `TupleOrVec` is not designed with the intent of being implementable, but
196/// there's nothing stopping you from doing so.
197///
198/// [crate-level documentation]: index.html
199/// [repeated tuples]: trait.RepeatedTuple.html
200pub trait TupleOrVec<E> {
201 /// Converts the type to a vec
202 fn as_vec(self) -> Vec<E>;
203}
204
205impl<E> TupleOrVec<E> for Vec<E> {
206 fn as_vec(self) -> Vec<E> {
207 self
208 }
209}
210
211macro_rules! impl_tuple {
212 (
213 $E:ident,
214 ($tup_head:ident, $($tup:ident),+),
215 $idx_head:tt @ $($idx:tt)@+
216 ) => {
217 impl<$E> RepeatedTuple<$E> for ($tup_head, $($tup),+) {
218 fn to_boxed_slice_reversed(self) -> Box<[$E]> {
219 Box::new([self.$idx_head, $(self.$idx),+])
220 }
221 }
222
223 impl<$E> TupleOrVec<$E> for ($tup_head, $($tup),+) {
224 fn as_vec(self) -> Vec<$E> {
225 RepeatedTuple::to_vec(self)
226 }
227 }
228
229 impl_tuple! {
230 $E,
231 ($($tup),+),
232 $($idx)@+
233 }
234 };
235
236 // base case
237 (
238 $E:ident,
239 ($tup:ident),
240 $idx:tt
241 ) => {
242 impl<$E> RepeatedTuple<$E> for ($tup,) {
243 fn to_boxed_slice_reversed(self) -> Box<[$E]> {
244 Box::new([self.$idx])
245 }
246 }
247
248 impl<$E> TupleOrVec<$E> for ($tup,) {
249 fn as_vec(self) -> Vec<$E> {
250 RepeatedTuple::to_vec(self)
251 }
252 }
253 }
254}
255
256impl_tuple! {
257 E,
258
259 (E, E, E, E,
260 E, E, E, E,
261 E, E, E, E,
262 E, E, E, E,
263
264 E, E, E, E,
265 E, E, E, E,
266 E, E, E, E,
267 E, E, E, E,
268
269 E, E, E, E,
270 E, E, E, E,
271 E, E, E, E,
272 E, E, E, E,
273
274 E, E, E, E,
275 E, E, E, E,
276 E, E, E, E,
277 E, E, E, E),
278
279
280 63 @ 62 @ 61 @ 60 @
281 59 @ 58 @ 57 @ 56 @
282 55 @ 54 @ 53 @ 52 @
283 51 @ 50 @ 49 @ 48 @
284
285 47 @ 46 @ 45 @ 44 @
286 43 @ 42 @ 41 @ 40 @
287 39 @ 38 @ 37 @ 36 @
288 35 @ 34 @ 33 @ 32 @
289
290 31 @ 30 @ 29 @ 28 @
291 27 @ 26 @ 25 @ 24 @
292 23 @ 22 @ 21 @ 20 @
293 19 @ 18 @ 17 @ 16 @
294
295 15 @ 14 @ 13 @ 12 @
296 11 @ 10 @ 9 @ 8 @
297 7 @ 6 @ 5 @ 4 @
298 3 @ 2 @ 1 @ 0
299}
300
301#[cfg(test)]
302mod tests {
303 use crate::RepeatedTuple;
304
305 #[rustfmt::skip]
306 macro_rules! long {
307 (tuple) => {
308 (
309 1, 2, 3, 4, 5, 6, 7, 8,
310 9, 10, 11, 12, 13, 14, 15, 16,
311 17, 18, 19, 20, 21, 22, 23, 24,
312 25, 26, 27, 28, 29, 30, 31, 32,
313 33, 34, 35, 36, 37, 38, 39, 40,
314 41, 42, 43, 44, 45, 46, 47, 48,
315 49, 50, 51, 52, 53, 54, 55, 56,
316 57, 58, 59, 60, 61, 62, 63, 64,
317 )
318 };
319
320 (slice_reversed) => {
321 [
322 64, 63, 62, 61, 60, 59, 58, 57,
323 56, 55, 54, 53, 52, 51, 50, 49,
324 48, 47, 46, 45, 44, 43, 42, 41,
325 40, 39, 38, 37, 36, 35, 34, 33,
326 32, 31, 30, 29, 28, 27, 26, 25,
327 24, 23, 22, 21, 20, 19, 18, 17,
328 16, 15, 14, 13, 12, 11, 10, 9,
329 8, 7, 6, 5, 4, 3, 2, 1,
330 ]
331 };
332 }
333
334 #[test]
335 fn to_boxed_slice_reversed() {
336 let t = (1,);
337 let b = t.to_boxed_slice_reversed();
338 assert!(b == Box::new([1]));
339
340 let t = (1, 2, 3);
341 let b = t.to_boxed_slice_reversed();
342 assert!(b == Box::new([3, 2, 1]));
343
344 let t = long!(tuple);
345 let b = t.to_boxed_slice_reversed();
346 assert!(b == Box::new(long!(slice_reversed)));
347 }
348
349 #[test]
350 fn to_boxed_slice() {
351 let t = (1, 2, 3);
352 let b = t.to_boxed_slice();
353 assert!(b == Box::new([1, 2, 3]));
354 }
355
356 #[test]
357 fn to_vec() {
358 let t = (1, 2, 3);
359 let v = t.to_vec();
360 assert_eq!(v, [1, 2, 3]);
361 }
362
363 #[test]
364 fn to_vec_reversed() {
365 let t = (1, 2, 3);
366 let v = t.to_vec_reversed();
367 assert_eq!(v, [3, 2, 1]);
368 }
369}