tiny_fn/
lib.rs

1//!
2//! Have you ever being placing closure into [`Box<dyn Fn(...)>`] and wondered:
3//! "Is there a crate to avoid heap allocations for small closures?"
4//!
5//! Wonder no more, this is the crate.
6//!
7//! # How to use
8//!
9//! This crate provides declarative macro [`tiny_fn!`] to generate closure wrappers
10//! able store closure erasing its type.
11//!
12//! Generated closure wrappers avoid heap allocations when wrapped closure fits inline storage.
13//!
14//! The macro is designed to be easy to write with simple syntax that mostly reuse constructs already existing in Rust.\
15//! Behavior of generated wrappers should be obvious from the first glance.
16//!
17//! # Example
18//!
19//! ```
20//! # use tiny_fn::tiny_fn;
21//! tiny_fn! {
22//!     struct Foo = Fn(a: i32, b: i32) -> i32;
23//! }
24//!
25//! let foo: Foo = Foo::new(|a, b| a + b);
26//! assert_eq!(3, foo.call(1, 2));
27//! ```
28//!
29//! Macro expands to `struct Foo` definition with two public methods.
30//!
31//! * `Foo::new` accepts any value that implements [`Fn(i32, i32) -> i32`] and returns new instance of `Foo`.
32//! * `Foo::call` follows signature specified to the macro. e.g. `Foo::call` accepts `a: i32` and `b: i32` and returns [`i32`].\
33//!   Plainly `Foo::call` calls closure from which this instance of `Foo` was crated using `a` and `b` arguments at the same positions.
34//!
35//! [`tiny_fn!`] macro supports defining multiple items at once.
36//!
37//! ```
38//! # use tiny_fn::tiny_fn;
39//! tiny_fn! {
40//!     struct Foo = Fn(a: i32, b: i32) -> i32;
41//!     struct Bar = Fn() -> String;
42//! }
43//!
44//! let foo: Foo = Foo::new(|a, b| a + b);
45//! let bar: Bar = Bar::new(|| "Hello, World!".to_string());
46//!
47//! assert_eq!(foo.call(1, 2), 3);
48//! assert_eq!(bar.call(), "Hello, World!");
49//! ```
50//!
51//! # Visibility
52//!
53//! [`tiny_fn!`] macro supports visibility qualifiers.
54//!
55//!
56//! ```
57//! # use tiny_fn::tiny_fn;
58//! tiny_fn! {
59//!     pub struct Foo = Fn(a: i32, b: i32) -> i32;
60//!     struct Bar = Fn() -> String;
61//!     pub(crate) struct Baz = Fn();
62//! }
63//! ```
64//!
65//! # Attributes
66//!
67//! [`tiny_fn!`] macro supports item attributes, including documentation.
68//!
69//! ```
70//! # use tiny_fn::tiny_fn;
71//! tiny_fn! {
72//!     /// This is `Foo` wrapper for that takes two `i32`s and return `i32`.
73//!     pub struct Foo = Fn(a: i32, b: i32) -> i32;
74//! }
75//! ```
76//!
77//! # [`Fn*`] traits family
78//!
79//! [`tiny_fn!`] macro can generate closure wrappers for any of the [`Fn*`] traits family.
80//!
81//! ```
82//! # use tiny_fn::tiny_fn;
83//! tiny_fn! {
84//!     struct A = Fn();
85//!     struct B = FnMut();
86//!     struct C = FnOnce();
87//! }
88//!
89//! let a = 42;
90//! let a: A = A::new(|| println!("{}", a));
91//! a.call();
92//! a.call();
93//!
94//! let mut b = 42;
95//! let mut b: B = B::new(|| b += 1);
96//! b.call();
97//! b.call();
98//!
99//! let c = String::from("Hello, World!");
100//! let c: C = C::new(move || println!("{}", c));
101//! c.call();
102//! // c.call(); // This will not compile, because `C` can be called only once.
103//! ```
104//!
105//! * `A` can wrap only closures that are callable when immutably borrowed. And so `A::call` takes `&self`.
106//! * `B` can wrap only closures that are callable when borrowed. And so `B::call` takes `&mut self`.
107//! * `C` can wrap any closures, even ones that are callable once. And so `C::call` takes `self`.
108//!
109//! # Generics
110//!
111//! Closure wrappers can be declared generic over number of types and those types should be used in function signature.
112//!
113//! ```
114//! # use tiny_fn::tiny_fn;
115//! tiny_fn! {
116//!     struct BinOp<T> = Fn(a: T, b: T) -> T;
117//! }
118//!
119//! let add: BinOp<i32> = BinOp::new(|a, b| a + b);
120//! let mul: BinOp<i32> = BinOp::new(|a, b| a * b);
121//!
122//! assert_eq!(mul.call(add.call(1, 2), 3), 9);
123//! ```
124//!
125//! Here `BinOp` is generic over `T`.\
126//! `BiOp::<T>::new` accepts closures bounds by [`Fn(T, T) -> T`].
127//!
128//! Notably `T` is not constrained by traits in `BinOp`.\
129//! Closure wrappers only move arguments and return values, so they don't need to know anything else about the type.
130//!
131//! # Markers
132//!
133//! Closure wrappers can be declared with marker traits.
134//! Simply add `|` and list of `+` prefixed marker traits after function signature.
135//! In Fn traits family `|` symbol is not used, but here it is required due to declarative macro limitations.
136//!
137//! They will be added to bounds on contained types.
138//! And if autotraits, they will be implemented for the wrapper type as well.
139//!
140//! ```rust
141//! # use tiny_fn::tiny_fn;
142//! tiny_fn! {
143//!     struct Foo = Fn(a: i32, b: i32) -> i32 | + Send;
144//! }
145//!
146//! let foo: Foo = Foo::new(|a, b| a + b);
147//!
148//! std::thread::spawn(move || {
149//!   foo.call(1, 2);
150//! });
151//! ```
152//! # Special generic parameters
153//!
154//! Closure wrapper generated by [`tiny_fn!`] macro always have two generic parameters besides generic types specified by macro caller:
155//! * Lifetime `'closure`.\
156//!   Wrapper contains closures bound by `'closure` lifetime.
157//! * Constant `INLINE_SIZE: usize`.\
158//!   Closures with size up to `INLINE_SIZE` and alignment requirement not exceeding [`tiny_fn::ALIGN`] will be inlined into wrapper structure directly.\
159//!   Otherwise heap allocation will occur.\
160//!   `INLINE_SIZE` parameter is defaulted to [`tiny_fn::DEFAULT_INLINE_SIZE`].
161//!
162//! [`Box<dyn Fn(...)>`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
163//! [`Fn(i32, i32) -> i32`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
164//! [`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
165//! [`Fn(T, T) -> T`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
166//! [`tiny_fn::ALIGN`]: `ALIGN`
167//! [`tiny_fn::DEFAULT_INLINE_SIZE`]: `DEFAULT_INLINE_SIZE`
168
169#![no_std]
170
171extern crate alloc;
172
173/// Inline storage alignment.
174/// Closures with stricter alignment requirements will be heap allocated by wrapper
175/// even if size fits.
176pub const ALIGN: usize = core::mem::align_of::<crate::private::InlineStorage<1>>();
177
178/// Default value for `INLINE_SIZE` parameter of closure wrappers generated by [`tiny_fn!`] macro.
179pub const DEFAULT_INLINE_SIZE: usize = core::mem::size_of::<[usize; 3]>();
180
181/// Content of this module is not public API.
182/// It is used by macro output.
183#[doc(hidden)]
184pub mod private {
185    pub use alloc::boxed::Box;
186    pub use core::{
187        mem::{align_of, size_of, transmute, ManuallyDrop, MaybeUninit},
188        ptr::{copy_nonoverlapping, drop_in_place, read, NonNull},
189    };
190
191    pub trait Closure {
192        type Inner;
193    }
194
195    #[repr(C)]
196    pub struct VTable<D = unsafe fn(), C = unsafe fn()> {
197        pub drop: D,
198        pub call: C,
199    }
200
201    #[repr(C, align(16))]
202    #[derive(Clone, Copy)]
203    pub struct InlineStorage<const INLINE_SIZE: usize> {
204        pub storage: MaybeUninit<[u8; INLINE_SIZE]>,
205    }
206
207    pub type StoragePtr<const INLINE_SIZE: usize> = NonNull<InlineStorage<INLINE_SIZE>>;
208
209    pub type DropFn<const INLINE_SIZE: usize> = unsafe fn(StoragePtr<INLINE_SIZE>);
210}
211
212#[doc(hidden)]
213#[macro_export]
214macro_rules! private_tiny_fn {
215    (@call $(<$($hrl:lifetime),+>)? Fn $(< $($lt:lifetime,)*  $($t:ident,)* >)? ($($arg_name:ident: $arg_type:ty),* $(,)?) $( -> $ret:ty)?) => {
216        fn call $(< $($hrl),+ >)? (&self, $($arg_name: $arg_type),*) $(-> $ret)? {
217            unsafe {
218                match self.vtable {
219                    None => (*self.payload.boxed.closure)($($arg_name),*),
220                    Some(vtable) => {
221                        let call_fn: CallFn< $($($lt,)* $($t,)*)? INLINE_SIZE> = $crate::private::transmute(vtable.call);
222                        call_fn(
223                            $crate::private::NonNull::from(&self.payload.inline),
224                            $($arg_name),*
225                        )
226                    }
227                }
228            }
229        }
230    };
231
232    (@call $(<$($hrl:lifetime),+>)? FnMut $(< $($lt:lifetime,)*  $($t:ident,)* >)? ($($arg_name:ident: $arg_type:ty),* $(,)?) $( -> $ret:ty)?) => {
233        fn call $(< $($hrl),+ >)? (&mut self, $($arg_name: $arg_type),*) $(-> $ret)? {
234            unsafe {
235                match self.vtable {
236                    None => (*(*self.payload.boxed).closure)($($arg_name),*),
237                    Some(vtable) => {
238                        let call_fn: CallFn< $($($lt,)* $($t,)*)? INLINE_SIZE> = $crate::private::transmute(vtable.call);
239                        call_fn(
240                            $crate::private::NonNull::from(&mut self.payload.inline),
241                            $($arg_name),*
242                        )
243                    }
244                }
245            }
246        }
247    };
248
249    (@call $(<$($hrl:lifetime),+>)? FnOnce $(< $($lt:lifetime,)*  $($t:ident,)* >)? ($($arg_name:ident: $arg_type:ty),* $(,)?) $( -> $ret:ty)?) => {
250        fn call $(< $($hrl),+ >)? (self, $($arg_name: $arg_type),*) $(-> $ret)? {
251            let mut me = $crate::private::ManuallyDrop::new(self);
252            unsafe {
253                match me.vtable {
254                    None => ($crate::private::ManuallyDrop::take(&mut me.payload.boxed).closure)($($arg_name),*),
255                    Some(vtable) => {
256                        let call_fn: CallFn< $($($lt,)* $($t,)*)? INLINE_SIZE> = $crate::private::transmute(vtable.call);
257                        call_fn(
258                            $crate::private::NonNull::from(&mut (*me).payload.inline),
259                            $($arg_name),*
260                        )
261                    }
262                }
263            }
264        }
265    };
266
267
268    (@call_outer $(<$($hrl:lifetime),+>)? Fn $(< $($lt:lifetime,)*  $($t:ident,)* >)? ($($arg_name:ident: $arg_type:ty),* $(,)?) $( -> $ret:ty)?) => {
269        /// Calls wrapped closure
270        /// and returns it result.
271        #[allow(dead_code)]
272        pub fn call $(<$($hrl),+>)? (&self, $($arg_name: $arg_type),*) $(-> $ret)? {
273            self.inner.call($($arg_name,)*)
274        }
275    };
276
277    (@call_outer $(<$($hrl:lifetime),+>)? FnMut $(< $($lt:lifetime,)*  $($t:ident,)* >)? ($($arg_name:ident: $arg_type:ty),* $(,)?) $( -> $ret:ty)?) => {
278        /// Calls wrapped closure
279        /// and returns it result.
280        #[allow(dead_code)]
281        pub fn call $(<$($hrl),+>)? (&mut self, $($arg_name: $arg_type),*) $(-> $ret)? {
282            self.inner.call($($arg_name,)*)
283        }
284    };
285
286    (@call_outer $(<$($hrl:lifetime),+>)? FnOnce $(< $($lt:lifetime,)*  $($t:ident,)* >)? ($($arg_name:ident: $arg_type:ty),* $(,)?) $( -> $ret:ty)?) => {
287        /// Calls wrapped closure
288        /// and returns it result.
289        #[allow(dead_code)]
290        pub fn call $(<$($hrl),+>)? (self, $($arg_name: $arg_type),*) $(-> $ret)? {
291            self.inner.call($($arg_name,)*)
292        }
293    };
294
295    (@inline_call_cast Fn $ptr:ident) => {
296        (*(*$ptr.as_ptr()).storage.as_ptr().cast::<F>())
297    };
298
299    (@inline_call_cast FnMut $ptr:ident) => {
300        (*(*$ptr.as_ptr()).storage.as_mut_ptr().cast::<F>())
301    };
302
303    (@inline_call_cast FnOnce $ptr:ident) => {
304        $crate::private::read((*$ptr.as_ptr()).storage.as_mut_ptr().cast::<F>())
305    };
306
307    // (@get_self_bounds [$($lifetimes:lifetime)*] [$($traits:path)*] [$(,)?]) => {
308    //     $($traits +)* $($lifetimes +)* 'closure
309    // };
310
311    // (@get_self_bounds [$($lifetimes:lifetime)*] [$($traits:path)*] [Self: $(+ $self_lifetimes:lifetime)* $(+ $self_traits:path)*  $(, $tail:tt: $(+ $tail_lifetimes:lifetime)* $(+ $tail_traits:path)*,),* $(,)?]) => {
312    //     $crate::private_tiny_fn!(@get_self_bounds [$($lifetimes)* $($self_lifetimes)*] [$($traits)* $($self_traits)*] [$( $($tail: $(+ $tail_lifetimes)* $(+ $tail_traits)*),* )*])
313    // };
314
315    // (@get_self_bounds [$($lifetimes:lifetime)*] [$($traits:path)*] [$not_self:tt: $(+ $not_self_lifetimes:lifetime)* $(+ $not_self_traits:path)*  $(, $tail:tt: $(+ $tail_lifetimes:lifetime)* $(+ $tail_traits:path)*,),* $(,)?]) => {
316    //     $crate::private_tiny_fn!(@get_self_bounds [$($lifetimes)*] [$($traits)*] [$( $($tail: $(+ $tail_lifetimes)* $(+ $tail_traits)*),* )*])
317    // };
318
319    // (@get_generic_bounds [$($lt:ident: $lifetimes:lifetime,)*] [$($tt:ident: $traits:path,)*] []) => {
320    //     $($lt : $lifetimes,)*
321    //     $($tt : $traits,)*
322    //     Self: 'closure,
323    // };
324
325    // (@get_generic_bounds [$($lt:ident: $lifetimes:lifetime,)*] [$($tt:ident: $traits:path,)*] [Self: $(+ $self_lifetimes:lifetime)* $(+ $self_traits:path)*  $(, $tail:tt: $(+ $tail_lifetimes:lifetime)* $(+ $tail_traits:path)*,),* $(,)?]) => {
326    //     $crate::private_tiny_fn!(@get_generic_bounds [$($lt: $lifetimes,)*] [$($tt: $traits,)*] [$( $($tail: $(+ $tail_lifetimes)* $(+ $tail_traits)*),* )*])
327    // };
328
329    // (@get_generic_bounds [$($lt:ident: $lifetimes:lifetime,)*] [$($tt:ident: $traits:path,)*] [$not_self:tt: $(+ $not_self_lifetimes:lifetime)* $(+ $not_self_traits:path)*  $(, $tail:tt: $(+ $tail_lifetimes:lifetime)* $(+ $tail_traits:path)*,),* $(,)?]) => {
330    //     $crate::private_tiny_fn!(@get_generic_bounds [$($lt: $lifetimes,)* $($not_self: $not_self_lifetimes,)*] [$($tt: $traits,)* $($not_self: $not_self_traits.)*] [$( $($tail: $(+ $tail_lifetimes)* $(+ $tail_traits)*),* )*])
331    // };
332}
333
334/// Defines new structure type.
335/// This type will be constructible from any closure
336/// that implement specified [`Fn*`] trait.
337/// Closures that fits into inline storage will be placed there.
338/// Otherwise closure will be boxed.
339/// And it will have `call` method with the same signature as the closure.
340///
341/// Defined type can have generic parameters that can be used in function signature.
342/// It will always have additional `const INLINE_SIZE: usize` generic parameter,
343/// that controls size of the inline storage. Alignment of the inline storage is hardcoded to 16
344/// which is enough for any primitive type and thus fits most of the types.
345///
346/// [`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
347#[macro_export]
348macro_rules! tiny_fn {
349    ($(
350        $(#[$meta:meta])*
351        $vis:vis struct $name:ident $(< $($lt:lifetime),* $(,)? $($t:ident),* >)? =
352        $(<$($hrl:lifetime),+>)?
353        $fun:ident ($(
354            $arg_name:ident: $arg_type:ty
355        ),* $(,)?)
356        $( -> $ret:ty)?
357        $(| $(+ $markers:path)+)?
358        $(where $( $wt:tt: $(+ $wtl:lifetime)* $(+ $wtb:path)* ),*)?
359        ;
360    )*) => {
361        $(
362            const _: () = {
363                type CallFn< $($($lt,)* $($t,)*)? const INLINE_SIZE: usize> = $(for<$($hrl),+>)? unsafe fn($crate::private::StoragePtr<INLINE_SIZE>, $($arg_type),*) $( -> $ret)?;
364
365                struct BoxedFn <'closure $( $(, $lt)* $(, $t)*)?>
366                $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
367                {
368                    closure: $crate::private::Box<dyn $(for<$($hrl),+>)? $fun($($arg_type),*) $(-> $ret)? $($(+ $markers)+)? + 'closure>,
369                }
370
371                #[doc(hidden)]
372                union TinyClosurePayload<'closure, $($($lt,)* $($t,)*)? const INLINE_SIZE: usize>
373                $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
374                {
375                    inline: $crate::private::InlineStorage<INLINE_SIZE>,
376                    boxed: $crate::private::ManuallyDrop<BoxedFn<'closure $($(, $lt)* $(, $t)*)?>>,
377                }
378
379                pub struct TinyClosure<'closure, $($($lt,)* $($t,)*)? const INLINE_SIZE: usize>
380                $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
381                {
382                    vtable: Option<&'static $crate::private::VTable>,
383                    payload: TinyClosurePayload<'closure, $($($lt,)* $($t,)*)? INLINE_SIZE>,
384                }
385
386                impl<'closure, $($($lt,)* $($t,)*)? const INLINE_SIZE: usize> Drop for TinyClosure<'closure, $($($lt,)* $($t,)*)? INLINE_SIZE>
387                $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
388                {
389                    fn drop(&mut self) {
390                        unsafe {
391                            match self.vtable {
392                                None => $crate::private::ManuallyDrop::drop(&mut self.payload.boxed),
393                                Some(vtable) => {
394                                    let drop_fn: $crate::private::DropFn<INLINE_SIZE> = $crate::private::transmute(vtable.drop);
395                                    drop_fn($crate::private::NonNull::from(&mut self.payload.inline));
396                                }
397                            }
398                        }
399                    }
400                }
401
402                impl<'closure, $($($lt,)* $($t,)*)? const INLINE_SIZE: usize> TinyClosure<'closure, $($($lt,)* $($t,)*)? INLINE_SIZE>
403                $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
404                {
405                    fn new<F>(f: F) -> Self
406                    where
407                        F: $(for<$($hrl),+>)? $fun ($($arg_type),*) $( -> $ret)? $($(+ $markers)+)? + 'closure,
408                    {
409                        let size_fits = $crate::private::size_of::<F>() <= INLINE_SIZE;
410                        let align_fits = $crate::private::align_of::<F>() <= $crate::ALIGN;
411
412                        if size_fits && align_fits {
413                            let mut inline = $crate::private::InlineStorage {
414                                storage: $crate::private::MaybeUninit::uninit(),
415                            };
416
417                            unsafe {
418                                let storage_f = inline.storage.as_mut_ptr() as *mut $crate::private::MaybeUninit<F>;
419                                (*storage_f).write(f);
420                            }
421
422                            let vtable = unsafe {
423                                $crate::private::transmute(&$crate::private::VTable::< $crate::private::DropFn<INLINE_SIZE>, CallFn< $($($lt,)* $($t,)*)? INLINE_SIZE>> {
424                                    drop: |ptr: $crate::private::StoragePtr<INLINE_SIZE>| {
425                                        $crate::private::drop_in_place(ptr.cast::<F>().as_ptr());
426                                    },
427                                    call: |ptr: $crate::private::StoragePtr<INLINE_SIZE> $(, $arg_name)*| {
428                                        $crate::private_tiny_fn!(@inline_call_cast $fun ptr)($($arg_name),*)
429                                    }
430                                })
431                            };
432                            TinyClosure {
433                                vtable: Some(vtable),
434                                payload: TinyClosurePayload {
435                                    inline,
436                                }
437                            }
438                        } else {
439                            let boxed_fn = BoxedFn {
440                                closure: $crate::private::Box::new(f),
441                            };
442
443                            TinyClosure {
444                                vtable: None,
445                                payload: TinyClosurePayload {
446                                    boxed: $crate::private::ManuallyDrop::new(boxed_fn),
447                                }
448                            }
449                        }
450                    }
451
452                    $crate::private_tiny_fn!(@call $(<$($hrl),+>)? $fun $(< $($lt,)*  $($t,)* >)? ($($arg_name: $arg_type),*) $( -> $ret)?);
453                }
454
455                impl<'closure, $($($lt,)* $($t,)*)? const INLINE_SIZE: usize> $crate::private::Closure for $name<'closure, $($($lt,)* $($t,)*)? INLINE_SIZE>
456                $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
457                {
458                    type Inner = TinyClosure<'closure, $($($lt,)* $($t,)*)? INLINE_SIZE>;
459                }
460            };
461
462            #[repr(transparent)]
463            $(#[$meta])*
464            $vis struct $name<'closure, $($($lt,)* $($t,)*)? const INLINE_SIZE: usize = { $crate::DEFAULT_INLINE_SIZE }>
465            $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
466            {
467                #[allow(dead_code)]
468                inner: < $name<'closure, $($($lt,)* $($t,)*)? INLINE_SIZE> as $crate::private::Closure>::Inner,
469            }
470
471            impl<'closure, $($($lt,)* $($t,)*)? const INLINE_SIZE: usize> $name<'closure, $($($lt,)* $($t,)*)? INLINE_SIZE>
472            $(where $( $($wt: $wtl,)* $($wt: $wtb,)* ),*)?
473            {
474                /// Constructs new instance of wrapper from specified closure.
475                /// Closure will be boxed if it doesn't fit inline storage.
476                #[inline]
477                pub fn new<F>(f: F) -> Self
478                where
479                    F: $(for<$($hrl),+>)? $fun ($($arg_type),*) $( -> $ret)? $($(+ $markers)+)? + 'closure,
480                {
481                    $name {
482                        inner: < < $name < $($($t,)*)? INLINE_SIZE > as $crate::private::Closure>::Inner>::new(f),
483                    }
484                }
485
486                $crate::private_tiny_fn!(@call_outer $(<$($hrl),+>)? $fun $(< $($lt,)*  $($t,)* >)? ($($arg_name: $arg_type),*) $( -> $ret)?);
487            }
488        )*
489    };
490}
491
492#[cfg(feature = "example")]
493pub mod example {
494    //! This module contains a few example declarations with [`tiny_fn!`] macro.\
495    //! And their usage in the section below.
496    //!
497    //! # Usage
498    //!
499    //! ```
500    //! # extern crate alloc;
501    //! # use tiny_fn::example::*;
502    //! # use alloc::{borrow::ToOwned, format};
503    //! #
504    //! let binop: BinOp::<i32, 24> = BinOp::new(|a, b| a + b);
505    //! assert_eq!(3, binop.call(1, 2));
506    //!
507    //! let a = "Hello".to_owned();
508    //! let b = "world".to_owned();
509    //! let make_string = MakeString::<32>::new(move || format!("{a} {b}!"));
510    //!
511    //! assert_eq!(make_string.call(), "Hello world!");
512    //! ```
513
514    tiny_fn! {
515        /// Contains any binary operation for a specific type.
516        /// That takes two values of that type
517        /// and returns value of that type.
518        pub struct BinOp<T> = Fn(a: T, b: T) -> T;
519
520        pub struct MakeString = Fn() -> alloc::string::String;
521    }
522}
523
524#[cfg(test)]
525mod tests {
526    use alloc::string::String;
527
528    #[test]
529    fn test() {
530        use alloc::rc::Rc;
531
532        tiny_fn! {
533            pub(crate) struct Foo<T> = FnMut(a: &T, b: T) -> T | + Send;
534            pub struct Bar = FnOnce(b: u8) -> alloc::string::String;
535            pub struct Baz<'a> = Fn(a: &'a str) -> &'a str | + Send;
536            pub struct Hrl = <'a> Fn(a: &'a str) -> &'a str | + Send;
537            struct Complex<'a, A, B> = <'b> Fn(a: &'a A, b: &'b B) -> &'a str | + Send where A: + 'a;
538        }
539
540        let mut x = 3;
541        let mut foo = Foo::<u8>::new(|a, b| {
542            x += a + b;
543            x
544        });
545        let bar: Bar<0> = Bar::new(|b| alloc::format!("{}", b));
546
547        assert_eq!(6, foo.call(&1, 2));
548        assert_eq!(13, foo.call(&3, 4));
549
550        assert_eq!("3", bar.call(3));
551
552        fn assert_send<T: Send>() {}
553        assert_send::<Foo<Rc<u8>>>();
554        let s = String::new();
555
556        let baz: Baz = Baz::new(|a: &str| a);
557
558        let _: &'static str = baz.call("Hello, World!");
559
560        // let _: &str = baz.call(&s); // This will not compile, because `s` is not `'static` and `baz` is bound to `'static` lifetime.
561
562        let mut hlp: Hrl = Hrl::new(|a: &str| a);
563
564        let _: &'static str = hlp.call("Hello, World!");
565
566        hlp = Hrl::new(|_| "foo");
567        let _: &str = hlp.call(&s); // This compiles because argument's lifetime is not bound by the type.
568
569        let complex: Complex<u8, u8> = Complex::new(|a: &u8, b: &u8| {
570            if *a > *b {
571                "a is greater"
572            } else {
573                "b is greater"
574            }
575        });
576    }
577}
578