存储闭包以重复使用的惯用方法?

时间:2017-01-19 09:22:17

标签: rust closures lifetime

在Rust中传递闭包是非常简单的,但是当存储闭包以供重用时,有多种解决方案(使用通用函数类型,引用闭包或框,具有myTableData[indexPath.row]生命周期的框?...)

虽然我使用不同类型的盒装类型多次混淆了这种情况,但请阅读类似的Q& A,甚至可能会有机会回答这个问题。我不知道如何处理这个问题,即使对于简单/明显的情况,也不是一个好的起点。

为了使问题更具体,使这个示例存储函数以便重用的好方法是什么,使用构建器模式存储将在以后调用的闭包。

'static

2 个答案:

答案 0 :(得分:3)

对此的惯用解决方案是封装封装。虽然装箱闭包并稍后调用它会产生分配开销和动态调度开销,但在大多数情况下它可以忽略不计,MyAction类型可以使用友好的错误消息轻松使用。

或者,不同的函数可以是MyAction结构的通用字段,它存储闭包而没有间接。在某些情况下,这会产生巨大的加速,但由于更复杂的错误消息以及无法自由移动MyAction个对象,这种类型的可用性会降低。

如果盒装版本在分析中清晰显示为慢,那么您可以转到通用版本。否则我建议继续使用易于使用的盒装版本。

  

带有'静态生命周期的盒子?

同样,为简单起见,您可以使用'static生命周期,但是您的MyAction结构只能存储不借用其环境的闭包。如果您在MyAction结构上使用生命周期并将其转发到闭包,那么您将能够以通用参数为代价借用您的环境,这可能最终会导致MyAction结构更难正确使用。

答案 1 :(得分:2)

为了完整起见,由于关闭所有权的一些语法并不明显,这是我尝试在问题中的代码的惯用/质朴版本。

(如果有任何问题,请直接评论或更新)。

  • 使用Boxed闭包,一般来说这似乎是首选方法,因为它意味着可以在同一个结构中存储多个不同的闭包类型。
  • 不使用'static,这允许调用者在其环境中使用变量(请参阅问题中的num_other使用)。而是在struct上使用生命周期。

使用盒装闭包继承容器结构生命周期的工作示例:

use std::boxed::Box;

struct MyActions<'a> {
    num: i32,
    times: i32,

    update_fn:  Option<Box<Fn(i32) -> i32 + 'a>>,
    twiddle_fn: Option<Box<Fn(i32) -> i32 + 'a>>,
}

impl <'a> MyActions<'a> {
    pub fn new(num: i32) -> Self {
        return MyActions {
            num: num,
            times: 1,
            update_fn: None,
            twiddle_fn: None,
        }
    }

    pub fn build(self) -> i32 {
        let mut num = self.num;

        if let Some(update_fn) = self.update_fn {
            for _ in 0..self.times {
                num = update_fn(num);
            }
        }

        if let Some(twiddle_fn) = self.twiddle_fn {
            for _ in 0..self.times {
                num = twiddle_fn(num);
            }
        }

        return num;
    }

    pub fn num_times(mut self, times: i32) -> Self {
        self.times = times;
        self
    }

    pub fn num_update<F>(mut self, func: F) -> Self
        where
        F: 'a,
        F: Fn(i32) -> i32,
    {
        self.update_fn = Some(Box::new(func));
        self
    }

    pub fn num_twiddle<F>(mut self, func: F) -> Self
        where
        F: 'a,
        F: Fn(i32) -> i32,
    {
        self.twiddle_fn = Some(Box::new(func));
        self
    }
}

// no changes needed here
fn main() {
    let act = MyActions::new(133);
    let num_other: i32 = 4;

    // builder pattern (currently executes immediately).
    let result = act
        .num_times(8)
        .num_update(|x| x * 2 + num_other)
        .num_twiddle(|x| (((x / 2) - 1) ^ (x + 1)) ^ num_other)
        .build();

    println!("done: {}", result);

    // Lets say we would want this example to work,
    // where 'times' is set after defining both functions.
    let act = MyActions::new(133);
    let result = act
        .num_update(|x| x * 2 + num_other)
        .num_twiddle(|x| (((x / 2) - 1) ^ (x + 1)) ^ num_other)
        .num_times(8)  // <-- order changed here
        .build();

    println!("done: {}", result);
}