fn_zip/lib.rs
1#![no_std]
2#![feature(unboxed_closures)]
3#![feature(tuple_trait)]
4#![feature(const_trait_impl)]
5#![feature(fn_traits)]
6#![cfg_attr(feature = "async", feature(async_fn_traits))]
7
8//! Provides a zip trait for functions, allowing two functions to be combined at compile-time before being called.
9//! This is equivalent to `core::future::join!()`, but lazy, and works for non-async functions.
10//!
11//! The resulting function takes the arguments of both functions and return a tuple.
12//!
13//! # Example
14//!
15//! ```rust
16//! use fn_zip::*;
17//!
18//! fn a(x: f32) -> f64
19//! {
20//! (x as f64).sqrt()
21//! }
22//! fn b(x: u8) -> u8
23//! {
24//! x + 1
25//! }
26//! let ab = a.fn_zip(b); // (f32, u8) -> (f64, u8)
27//!
28//! let (x_a, x_b) = (4.0, 23);
29//!
30//! let (y_a, y_b) = ab(x_a, x_b);
31//!
32//! assert_eq!(y_a, a(x_a));
33//! assert_eq!(y_b, b(x_b));
34//! ```
35//!
36//! # Async
37//!
38//! The zipped functions can also implement [AsyncFnOnce](core::ops::AsyncFnOnce), [AsyncFnMut](core::ops::AsyncFnMut) and [AsyncFn](core::ops::AsyncFn) if both functions qualify.
39//!
40//! This is an experimental feature, since it just recently (as of writing) got added to the rust core library on rust-nightly, and may be subject to change at any point. Enable it with feature `async` or `experimental`.
41//!
42#![cfg_attr(feature = "async", doc = r##"
43```rust
44#![feature(fn_traits)]
45#![feature(async_fn_traits)]
46
47use fn_zip::*;
48use core::ops::AsyncFn;
49
50async fn a(x: f32) -> f64
51{
52 (x as f64).sqrt()
53}
54async fn b(x: u8) -> u8
55{
56 x + 1
57}
58
59let ab = a.fn_zip(b);
60let (x_a, x_b) = (4.0, 23);
61
62# tokio_test::block_on(async {
63// I don't know of any prettier way to call an async function...
64
65let (y_a, y_b) = ab.async_call((x_a, x_b)).await;
66
67assert_eq!(y_a, a(x_a).await);
68assert_eq!(y_b, b(x_b).await);
69# })
70```"##)]
71//!
72//! Independent of this feature, it's still possible to zip two asyncronous functions normally, but their futures will not be joined.
73//!
74//! # Compile time function zipping
75//!
76//! Functions can also be zipped during compile-time.
77//!
78//! ```rust
79//! #![feature(const_trait_impl)]
80//!
81//! use fn_zip::*;
82//!
83//! fn a(x: f32) -> f64
84//! {
85//! (x as f64).sqrt()
86//! }
87//! fn b(x: u8) -> u8
88//! {
89//! x + 1
90//! }
91//!
92//! // Corce functions into function pointers
93//! const A: fn(f32) -> f64 = a;
94//! const B: fn(u8) -> u8 = b;
95//!
96//! // Zip during compile time
97//! const AB: ZippedFn<(f32,), (u8,), fn(f32) -> f64, fn(u8) -> u8> = A.fn_zip_once(B);
98//!
99//! let (x_a, x_b) = (4.0, 23);
100//! let (y_a, y_b) = AB(x_a, x_b);
101//!
102//! assert_eq!(y_a, a(x_a));
103//! assert_eq!(y_b, b(x_b));
104//! ```
105//!
106//! # Tuple sizes
107//!
108//! By default, this crate operates with function pairs of up to 16 arguments combined, and splits them up in the form of tuples. If you want to use differently sized tuples, use the features `8`, `16`, `32`, `64`, `96`, `128`, `160`, `192`, `224` or `256` to set the maximum supported tuple size.
109//!
110//! The `dont_hurt_yourself_by_using_all_features` is there to prevent usage of tuples bigger than 8 if `cargo` is ran with the flag `--all-features`. Using a tuple size above 16 is highly discouraged as it will make compilation time unbearably long. Compilation time will increase exponentially. You have been warned.
111
112moddef::moddef!(
113 flat(pub) mod {
114 zip,
115 zipped_fn,
116 join for cfg(feature = "async")
117 }
118);
119
120#[cfg(test)]
121mod tests
122{
123 use super::*;
124
125 #[test]
126 fn it_works()
127 {
128 fn a(x: f32) -> f64
129 {
130 (x as f64).sqrt()
131 }
132 fn b(x: u8) -> u8
133 {
134 x + 1
135 }
136
137 let mut ab = a.fn_zip(b);
138 let (x_a, x_b) = (4.0, 23);
139
140 let (y_a, y_b) = ab(x_a, x_b);
141
142 assert_eq!(y_a, a(x_a));
143 assert_eq!(y_b, b(x_b));
144
145 let ab = &mut ab;
146
147 let (y_a, y_b) = ab(x_a, x_b);
148
149 assert_eq!(y_a, a(x_a));
150 assert_eq!(y_b, b(x_b));
151
152 let ab = &*ab;
153
154 let (y_a, y_b) = ab(x_a, x_b);
155
156 assert_eq!(y_a, a(x_a));
157 assert_eq!(y_b, b(x_b));
158 }
159
160 #[cfg(feature = "async")]
161 #[test]
162 fn test_async()
163 {
164 use core::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce};
165
166 async fn a(x: f32) -> f64
167 {
168 (x as f64).sqrt()
169 }
170 async fn b(x: u8) -> u8
171 {
172 x + 1
173 }
174
175 let mut ab = a.fn_zip(b);
176 let (x_a, x_b) = (4.0, 23);
177
178 tokio_test::block_on(async {
179 // I don't know of any prettier way to call an async function...
180
181 let (y_a, y_b) = ab.async_call((x_a, x_b)).await;
182
183 assert_eq!(y_a, a(x_a).await);
184 assert_eq!(y_b, b(x_b).await);
185
186 let (y_a, y_b) = ab.async_call_mut((x_a, x_b)).await;
187
188 assert_eq!(y_a, a(x_a).await);
189 assert_eq!(y_b, b(x_b).await);
190
191 let (y_a, y_b) = ab.async_call_once((x_a, x_b)).await;
192
193 assert_eq!(y_a, a(x_a).await);
194 assert_eq!(y_b, b(x_b).await);
195 });
196 }
197
198 #[test]
199 fn test_const()
200 {
201 fn a(x: f32) -> f64
202 {
203 (x as f64).sqrt()
204 }
205 fn b(x: u8) -> u8
206 {
207 x + 1
208 }
209
210 // Corce functions into function pointers
211 const A: fn(f32) -> f64 = a;
212 const B: fn(u8) -> u8 = b;
213
214 // Zip during compile time
215 const AB: ZippedFn<(f32,), (u8,), fn(f32) -> f64, fn(u8) -> u8> = A.fn_zip_once(B);
216 let (x_a, x_b) = (4.0, 23);
217
218 let (y_a, y_b) = AB(x_a, x_b);
219
220 assert_eq!(y_a, a(x_a));
221 assert_eq!(y_b, b(x_b));
222 }
223}