带盒装与未装箱的结构

时间:2018-03-05 19:20:37

标签: rust

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

存储未装箱的关闭

Rust书中有一个简单的Cacher示例chapter on closures

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中添加,但类型参数在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箱子的作者,我想向用户展示{em>两个的Cacher实现,未装箱的Cacher和装箱的BCacher。但我不想两次编写实现。什么是最好的方法 - 如果有的话 - 使用现有的Cacher实现来实现BCacher

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

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

有没有办法实现GymFactory没有类型参数,包含 - 出于私人目的 - WorkoutFactory带有类型参数,可能存储在Box

摘要

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

0 个答案:

没有答案