Expand description
Have you ever being placing closure into Box<dyn Fn(...)>
and wondered:
“Is there a crate to avoid heap allocations for small closures?”
Wonder no more, this is the crate.
§How to use
This crate provides declarative macro tiny_fn!
to generate closure wrappers
able store closure erasing its type.
Generated closure wrappers avoid heap allocations when wrapped closure fits inline storage.
The macro is designed to be easy to write with simple syntax that mostly reuse constructs already existing in Rust.
Behavior of generated wrappers should be obvious from the first glance.
§Example
tiny_fn! {
struct Foo = Fn(a: i32, b: i32) -> i32;
}
Macro expands to struct Foo
definition with two public methods.
Foo::new
accepts any value that implementsFn(i32, i32) -> i32
and returns new instance ofFoo
.Foo::call
follows signature specified to the macro. e.g.Foo::call
acceptsa: i32
andb: i32
and returnsi32
.
PlainlyFoo::call
calls closure from which this instance ofFoo
was crated usinga
andb
arguments at the same positions.
tiny_fn!
macro supports defining multiple items at once.
tiny_fn! {
struct Foo = Fn(a: i32, b: i32) -> i32;
struct Bar = Fn() -> String;
}
§Visibility
tiny_fn!
macro supports visibility qualifiers.
tiny_fn! {
pub struct Foo = Fn(a: i32, b: i32) -> i32;
struct Bar = Fn() -> String;
pub(crate) struct Baz = Fn();
}
§Attributes
tiny_fn!
macro supports item attributes, including documentation.
tiny_fn! {
/// This is `Foo` wrapper for that takes two `i32`s and return `i32`.
pub struct Foo = Fn(a: i32, b: i32) -> i32;
}
§Fn*
traits family
tiny_fn!
macro can generate closure wrappers for any of the Fn*
traits family.
tiny_fn! {
struct A = Fn();
struct B = FnMut();
struct C = FnOnce();
}
A
can wrap only closures that are callable when immutably borrowed. And soA::call
takes&self
.B
can wrap only closures that are callable when borrowed. And soB::call
takes&mut self
.C
can wrap any closures, even ones that are callable once. And soC::call
takesself
.
§Generics
Closure wrappers can be declared generic over number of types and those types should be used in function signature.
tiny_fn! {
struct BinOp<T> = Fn(a: T, b: T) -> T;
}
Here BinOp
is generic over T
.
BiOp::<T>::new
accepts closures bounds by Fn(T, T) -> T
.
Notably T
is not constrained by traits in BinOp
.
Closure wrappers only move arguments and return values, so they don’t need to know anything else about the type.
§Special generic parameters
Closure wrapper generated by tiny_fn!
macro always have two generic parameters besides generic types specified by macro caller:
- Lifetime
'closure
.
Wrapper contains closures bound by'closure
lifetime. - Constant
INLINE_SIZE: usize
.
Closures with size up toINLINE_SIZE
and alignment requirement not exceedingtiny_fn::ALIGN
will be inlined into wrapper structure directly.
Otherwise heap allocation will occur.
INLINE_SIZE
parameter is defaulted totiny_fn::DEFAULT_INLINE_SIZE
.
Modules§
- This module contains a few example declarations with
tiny_fn!
macro.
And their usage in the section below.
Macros§
- Defines new structure type. This type will be constructible from any closure that implement specified
Fn*
trait. Closures that fits into inline storage will be placed there. Otherwise closure will be boxed. And it will havecall
method with the same signature as the closure.
Constants§
- Inline storage alignment. Closures with stricter alignment requirements will be heap allocated by wrapper even if size fits.
- Default value for
INLINE_SIZE
parameter of closure wrappers generated bytiny_fn!
macro.