带盒装闭包与无盒封口的结构

  • 本文关键字:结构 盒装 闭包 rust
  • 更新时间 :
  • 英文 :


我仍在内化 Rust 中的闭包以及如何最好地使用它们,所以这个问题可能有些模糊,也许会有愚蠢的子问题。我基本上在寻找合适的习语,甚至可能改变我对如何在 Rust 中做一些事情的思考方式。

存放未装箱封口

Rust 书在闭包章节中有一个示例简单Cacher

struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
}
}
}
}

它应该像这样使用:

let mut c = Cacher::new(|a| a);
let v1 = c.value(1);

这很好且很有用,但是如果我需要让这个Cacher成为另一个结构的成员,比如(本着 Rust 书籍章节的精神),一个WorkoutFactory呢?由于Cacher是由闭包类型参数化的,因此我被迫使用相同的闭包类型参数化WorkoutFactory

我的理解正确吗?我想是的,Cacher结构结构取决于计算的类型T,所以结构WorkoutFactory结构必须取决于Cacher的类型。一方面,这感觉像是 Rust 中闭包工作方式的自然、不可避免的和完全合理的结果,另一方面,这意味着

  • WorkoutFactory可以包含在另一个结构中,该结构也由T强制参数化,该结构可以包含在另一个结构中,... - 闭合类型像瘟疫一样传播。由于其他T可能来自成员层次结构的深处,顶级结构的签名可能会变得可怕。
  • WorkoutFactory中涉及一些缓存的事实应该只是一个实现细节,也许缓存甚至在 2.0 版中添加了,但 type 参数在WorkoutFactory的公共接口中可见,需要考虑。看似实现的细节现在是界面的一部分,没有好的:(

有没有办法在不更改Cacher签名的情况下解决这些问题?其他人如何应对?

存储盒装封口

如果我想摆脱类型参数,我可以Box闭包。我想出了以下代码:

struct BCacher {
calculation: Box<Fn(u32) -> u32>,
value: Option<u32>,
}
impl BCacher {
fn new<T: Fn(u32) -> u32 + 'static>(calculation: T) -> BCacher {
BCacher {
calculation: Box::new(calculation),
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
}
}
}
}

我可以像Cacher一样使用它:

let mut c = BCacher::new(|a| a);
let v1 = c.value(1);

几乎:('static'注释意味着我不能这样做:

let x = 1;
let mut c = BCacher::new(|a| a + x);

因为关闭可能会比x寿命更长.不幸的是,非盒装版本可能实现的功能不再可能与盒装版本一起使用。

此外,这个版本效率较低,有必要取消引用Box(这是正确的吗?),并且RAM访问很慢。在大多数情况下,差异很可能可以忽略不计,但仍然..

我可以用生命周期注释解决第一个问题:

struct BLCacher<'a> {
calculation: Box<Fn(u32) -> u32 + 'a>,
value: Option<u32>,
}

但现在我又回到了类型参数及其所有不愉快的后果Cacher

选择权

这似乎是一个不幸的情况。我有两种方法可以在结构中存储闭包,每种方法都有自己的一组问题。假设我愿意接受这一点,作为令人敬畏的虚构Cacher箱的作者,我想向用户展示Cacher的实现,未装箱Cacher和盒装BCacher。但我不想两次编写实现。使用现有的Cacher实现来实现BCacher的最佳方法是什么(如果有的话)?

在相关的说明中(甚至可能是同一个问题),让我们假设我有一个

struct WorkoutFactory<T>
where
T: Fn(u32) -> u32,
{
cacher: Cacher<T>,
}

有没有办法在没有类型参数的情况下实现GymFactory,这些参数将包含 - 出于私有目的 -WorkoutFactory带有类型参数,可能存储在Box中?

总结

一个很长的问题,对不起。来自 Scala,在 Rust 中使用闭包就不那么简单了。我希望我已经解释了我还没有找到令人满意的答案的挣扎。

抱歉,不是完整的答案,但在这里的这段代码中:

let x = 1;
let mut c = BCacher::new(|a| a + x);

为什么不将其更改为:

let x = 1;
let mut c = BCacher::new(move |a| a + x);

这样,函子将吸收并拥有"x",因此不会有任何对它的引用,这应该有望解决所有其他问题。

最新更新