Rust 存储闭包以供重用的惯用方法?

Rust 存储闭包以供重用的惯用方法?,rust,closures,lifetime,Rust,Closures,Lifetime,在Rust中传递闭包非常简单,但是,当存储闭包以供重用时,有多种解决方案(使用通用函数类型、引用闭包或box、box with“staticlife or not?”) 虽然我已经用不同的盒装类型多次混淆了这一点,但仔细阅读类似的问答,甚至可以冒险回答这个问题。我不知道如何处理这个问题,即使是简单/明显的情况,也不知道什么是好的起点 为了使问题更具体,使用构建器模式存储稍后调用的闭包,使本示例存储函数以供重用的好方法是什么 // This example looks a bit long but

在Rust中传递闭包非常简单,但是,当存储闭包以供重用时,有多种解决方案(使用通用函数类型、引用闭包或box、box with
“static
life or not?”)

虽然我已经用不同的盒装类型多次混淆了这一点,但仔细阅读类似的问答,甚至可以冒险回答这个问题。我不知道如何处理这个问题,即使是简单/明显的情况,也不知道什么是好的起点

为了使问题更具体,使用构建器模式存储稍后调用的闭包,使本示例存储函数以供重用的好方法是什么

// This example looks a bit long but its really very simple.

// * This example is most of the way to implementing the builder pattern,
//   it ust runs the code immediately instead of storing input
//   to run on `build()`.
// * Changes should only be needed where the comment `stored closures:`
//   has been written.
// * I've attempted to make this example as generic as possible,
//   but not _so_ simple that the answer wont apply to real-world use (hopefully!).

struct MyActions {
    num: i32,
    times: i32,

    // stored closures: should be stored here.
    // update_fn: Option<Fn(...)>,
    // twiddle_fn: Option<Fn(...)>,
}

impl MyActions {
    pub fn new(num: i32) -> Self {
        return MyActions {
            num: num,
            times: 1,
        }
    }

    pub fn build(self) -> i32 {
        // stored closures:
        // should run both actions if they're defined and return the result.
        return self.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: Fn(i32) -> i32
    {
        // stored closures: run immediately for now
        for _ in 0..self.times {
            self.num = func(self.num);
        }
        self
    }

    pub fn num_twiddle<F>(mut self, func: F) -> Self
        where
        F: Fn(i32) -> i32
    {
        // stored closures: run immediately for now
        for _ in 0..self.times {
            self.num = func(self.num);
        }
        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();

    // Lets say we would want this example to work,
    // where 'times' is set after defining both functions.
    /*
    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);
}
//这个例子看起来有点长,但实际上非常简单。
//*此示例是实现生成器模式的大部分方法,
//它必须立即运行代码,而不是存储输入
//在“build()”上运行。
//*只有在注释“存储的闭包:`
//已经写好了。
//*我试图使这个例子尽可能通用,
//但答案并不简单,不适用于现实世界(希望如此!)。
结构MyActions{
num:i32,
时报:i32,,
//存储的闭包:应存储在此处。
//更新:选项,
//旋转fn:选项,
}
实施我的行动{
发布fn新(编号:i32)->Self{
返回我的操作{
num:num,
时报:1,,
}
}
发布fn构建(自)->i32{
//存储的闭包:
//如果定义了这两个操作,则应运行它们并返回结果。
返回self.num;
}
发布fn num_次(mut self,times:i32)->self{
self.times=次;
自己
}
发布fn num_更新(mut self,func:F)->self
哪里
F:Fn(i32)->i32
{
//存储的闭包:现在立即运行
对于0..self.times中的uu{
self.num=func(self.num);
}
自己
}
pub fn num_twidle(mut self,func:F)->self
哪里
F:Fn(i32)->i32
{
//存储的闭包:现在立即运行
对于0..self.times中的uu{
self.num=func(self.num);
}
自己
}
}
//这里不需要更改
fn main(){
让act=MyActions::new(133);
让num_other:i32=4;
//生成器模式(当前立即执行)。
让结果=行动
.num_次(8)
.num_更新(| x | x*2+num_其他)
.num_旋转(|x|)((x/2)-1^(x+1))^num_其他)
.build();
//假设我们想让这个例子起作用,
//其中“时间”是在定义两个函数后设置的。
/*
让结果=行动
.num_更新(| x | x*2+num_其他)
.num_旋转(|x|)((x/2)-1^(x+1))^num_其他)

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

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

如果盒装版本在评测中显示得很慢,那么您可以转到通用版本。否则,我建议继续使用易于使用的盒装版本

框是否具有“静态寿命”


同样,为了简单起见,您可以使用
静态的
生存期,但是您的
MyAction
结构只能存储不借用其环境的闭包。如果您在
MyAction
结构上使用生存期并将其转发给闭包,您将能够借用您的环境,而代价是使用泛型参数,这将导致同样,可能会使
MyAction
结构更难正确使用。

为了完整性,由于闭包所有权的某些语法不明显,下面是我对问题代码的惯用/粗俗版本的尝试

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

  • 使用盒式闭包,通常这似乎是首选方法,因为这意味着可以在同一个结构中存储多个不同的闭包类型
  • 不使用
    'static
    ,这允许调用者在其环境中使用变量(请参阅问题中的
    num\u other
    use)。而是在
    结构上使用生存期
使用继承容器结构生存期的盒装闭包的工作示例:

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);
}
使用std::boxed::Box;
结构MyActions>,
旋转fn:选项MyActions i32,
{
self.update_fn=Some(Box::new(func));
自己
}
pub fn num_twidle(mut self,func:F)->self
哪里
F:“a,
F:Fn(i32)->i32,
{
self.twidle_fn=Some(Box::new(func));
自己
}
}
//这里不需要更改
fn main(){
让act=MyActions::new(133);
让num_other:i32=4;
//生成器模式(当前立即执行)。
让结果=行动
.num_次(8)
.num_更新(| x | x*2+num_其他)
.num_旋转(|x|)((x/2)-1^(x+1))^num_其他)
.build();
println!(“完成:{}”,结果);
//假设我们想让这个例子起作用,
//其中“时间”是在定义两个函数后设置的。
让act=MyActions::new(133);
让结果=行动
.num_更新(| x | x*2+num_其他)
.num_旋转(|x|)((x/2)-1^(x+1))^num_其他)
.num_次(8)//