我有一个闭包,我构造它:
let r = receiver.clone();
let is_channel_empty = move || -> bool { r.is_empty() };
然后通过注入
Adapter::new(is_channel_empty)
适配器如下所示:
type ChannelEmptyFn = dyn Fn() -> bool + Send + Sync;
// for Box: type ChannelEmptyFn = Box<dyn Fn() -> bool + Send + Sync + 'static>;
pub struct Adapter {
is_channel_empty: ChannelEmptyFn,
}
impl Adapter {
pub fn new(is_channel_empty: ChannelEmptyFn) -> Self
{
Self { is_channel_empty }
}
}
我得到了一个明显的,运行时未知大小的错误。如果我通过了一个引用,它需要一辈子的时间,"静态"是行不通的。如果我传递一个Box,它会得到堆分配,虽然我不喜欢堆分配,但这是一个简单的解决方案。还有其他选择吗?我不能传递fn
(函数指针(,因为闭包有一个捕获的环境。有没有办法将字段类型指定为闭包?除了Fn
、FnMut
或FnOnce
?我只是想知道我对Rust的有限了解是否遗漏了什么。
最后一件事,适配器最终被包裹在一个Arc中,是的,这里有一些讽刺,这也是我希望将其全部分配在一个块中的另一个原因。
每个闭包都是自己的匿名类型,实现特定的闭包特性。这意味着每个闭包的实现方式与其他闭包不同,并且不能期望有单一的大小或方法。为了对此进行抽象,我们有两种选择:泛型或特征对象。
泛型是编译时的,而trait对象是动态的(运行时(,这会产生必要的运行时开销,Rust对此进行了明确说明。
问题是,泛型基本上只是在泛型的基础上生成一堆不同的类型,这意味着具有泛型闭包的结构与包含不同闭包的同一结构不是同一类型,即使这两个闭包具有相同的闭包特征,因为闭包具有不同的类型。
如果你不想用一个适配器换另一个适配器,那么泛型非常适合你。
pub struct Adapter<F: Fn() -> bool + Send + Sync> {
is_channel_empty: F,
}
impl<F: Fn() -> bool + Send + Sync> Adapter<F> {
pub fn new(is_channel_empty: F) -> Self
{
Self { is_channel_empty }
}
}
如果您希望能够在运行时将闭包或适配器交换为另一个闭包或适配器,则需要像现在这样动态。
您必须将闭包保留在一层间接层之后,以便可以安全地交换其大小。你可以走参考路线,但正如你所说,你需要一个生命周期,这对Arc不起作用,因为Arc需要拥有它的内容(静态生命周期(。拥有未知大小的东西的唯一方法是一个盒子,因为堆是动态的。如果你是动态的,你通常必须全程投入。如果堆分配是需要的,那么就没有办法绕过它。堆分配并不是坏事,它是工具箱中的另一个工具,在大多数关闭情况下不会产生太多成本,因为它只需要为动态调度表和捕获的变量(如果有的话(进行分配。
来自Arc:上的Rust文档
Arc类型提供了在堆中分配的T类型值的共享所有权