5 个版本
新增 0.1.7 | 2024 年 8 月 22 日 |
---|---|
0.1.6 | 2024 年 1 月 22 日 |
0.1.5 | 2022 年 9 月 2 日 |
0.1.4 | 2022 年 8 月 3 日 |
#142 in Rust 模式
4,633 下载/月
在 10 个crate中使用 (7 个直接使用)
27KB
246 代码行
TinyFn crate
动机
你是否曾经把闭包放入 Box<dyn Fn(...)>
并想知道:“有没有一个 crate 可以避免为小闭包进行堆分配?”
不再需要猜测,这就是这个 crate。
如何使用
这个 crate 提供了声明式宏 tiny_fn!
生成可以存储擦除其类型的闭包包装器。
生成的闭包包装器在包装的闭包适合内联存储时避免堆分配。
该宏设计得易于编写,语法简单,主要重用了 Rust 中已存在的结构。
生成的包装器的行为应该一目了然。
示例
tiny_fn! {
struct Foo = Fn(a: i32, b: i32) -> i32;
}
let foo: Foo = Foo::new(|a, b| a + b);
assert_eq!(foo.call(1, 2), 3);
宏展开为一个包含两个公共方法的 struct Foo
定义。
Foo::new
接受任何实现了Fn(i32, i32) -> i32
的值,并返回一个新的Foo
实例。Foo::call
遵循宏指定的签名。例如,Foo::call
接受a: i32
和b: i32
,并返回i32
。
简单来说,Foo::call
使用a
和b
参数(在同一位置)调用闭包,这些参数用于创建Foo
的这个实例。
tiny_fn!
宏支持一次性定义多个项目。
tiny_fn! {
struct Foo = Fn(a: i32, b: i32) -> i32;
struct Bar = Fn() -> String;
}
let foo: Foo = Foo::new(|a, b| a + b);
let bar: Bar = Bar::new(|| "Hello, World!".to_string());
assert_eq!(foo.call(1, 2), 3);
assert_eq!(bar.call(), "Hello, World!");
可见性
tiny_fn!
宏支持可见性限定符。
tiny_fn! {
pub struct Foo = Fn(a: i32, b: i32) -> i32;
struct Bar = Fn() -> String;
pub(crate) struct Baz = Fn();
}
属性
tiny_fn!
宏支持项目属性,包括文档。
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*
特性族
tiny_fn!
宏可以为 Fn*
特性族中的任何特性生成闭包包装器。
tiny_fn! {
struct A = Fn();
struct B = FnMut();
struct C = FnOnce();
}
let a = 42;
let a: A = A::new(|| println!("{}", a));
a.call();
a.call();
let mut b = 42;
let mut b: B = B::new(|| b += 1);
b.call();
b.call();
let c = String::from("Hello, World!");
let c: C = C::new(move || println!("{}", c));
c.call();
// c.call(); // This will not compile, because `C` can be called only once.
A
只能包装在不可变借用时可以调用的闭包。因此,A::call
接受&self
。B
只能包装在借用时可以调用的闭包。因此,B::call
接受&mut self
。C
可以包装任何闭包,甚至是一次性可调用的闭包。因此,C::call
接受self
。
泛型
闭包包装器可以声明为泛型,这些类型应该在函数签名中使用。
tiny_fn! {
struct BinOp<T> = Fn(a: T, b: T) -> T;
}
let add: BinOp<i32> = BinOp::new(|a, b| a + b);
let mul: BinOp<i32> = BinOp::new(|a, b| a * b);
assert_eq!(mul.call(add.call(1, 2), 3), 9);
在这里,BinOp
是对 T
泛型的。
BiOp::<T>::new
通过 Fn(T, T) -> T
接受闭包界限。
值得注意的是,在 BinOp
中,T
并不受特质的约束。
闭包包装器只移动参数和返回值,因此它们不需要了解有关类型的其他信息。
标记
闭包包装器可以声明为具有标记特性。只需在函数签名后添加 |
和由加号前缀的标记特性列表即可。在 Fn 特性族中,|
符号不被使用,但由于声明性宏的限制,这里需要它。
它们将被添加到包含类型的界限上。如果它们是自动特性,它们也将为包装器类型实现。
tiny_fn! {
struct Foo = Fn(a: i32, b: i32) -> i32 | + Send;
}
let foo: Foo = Foo::new(|a, b| a + b);
std::thread::spawn(move || {
foo.call();
});
特殊泛型参数
tiny_fn!
宏生成的闭包包装器除了宏调用者指定的泛型类型外,总是有两个泛型参数
- 生命周期
'closure
。
包装器包含由'closure
生命周期绑定的闭包。 - 常量
INLINE_SIZE: usize
。
大小不超过INLINE_SIZE
且对齐要求不超过tiny_fn::ALIGN
的闭包将直接内联到包装器结构中。
否则将发生堆分配。
INLINE_SIZE
参数默认为tiny_fn::DEFAULT_INLINE_SIZE
。
许可证
许可协议为以下之一
- Apache License, Version 2.0, (license/APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (license/MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非你明确声明,否则根据 Apache-2.0 许可证定义,你有意提交的任何贡献,包括在作品中包含的内容,都将按照上述协议双重许可,无需任何额外条款或条件。