修改并 return 关闭

Modify and return closure

我正在尝试创建一个函数来执行以下操作: 接受形式为 fn(T) -> T 的闭包 f return 形式为 fn(T, bool) -> T 的闭包,它根据 bool 参数有条件地执行 f

我来自 haskell 左右的背景,在 Haskell 这将是这样的:

conditionally :: (a -> a) -> a -> Bool -> a
conditionally f x True = f x
conditionally f x False = x

将其翻译成更像 rust 的东西:

conditionally :: ((t) -> t) -> ((t, Bool) -> t)
conditionally f = \(x, b) -> if b then (f x) else (x)

我在 rust 中尝试了以下方法:

fn conditionally<T>(f: &'static (dyn Fn(T) -> T + 'static)) -> Box<dyn Fn(T, bool) -> T> {
    Box::new(&|x, b| if b { f(x) } else { x } )
}

并被告知使用 move 关键字来确保闭包拥有 f 的所有权。但是,以下仍然不起作用:

fn conditional<T>(f: &'static (dyn Fn(T) -> T + 'static)) -> Box<dyn Fn(T, bool) -> T> {
    Box::new(&move|x, b| if b { f(x) } else { x } )
}

我收到以下错误(这也出现在添加 move 之前):

error[E0515]: cannot return value referencing temporary value
   --> src/main.rs:216:5
    |
216 |     Box::new(&move|x, b| if b { f(x) } else { x } )
    |     ^^^^^^^^^^-----------------------------------^^
    |     |         |
    |     |         temporary value created here
    |     returns a value referencing data owned by the current function

我认为 'data owned by the current function' 要么是我定义的闭包,要么是我移动的 f,但我无法理解这一切适合在一起。
作为冒烟检查,我确保我能够将我在函数体中定义的更简单的闭包装箱,并编译以下代码:

fn conditional_increment() -> Box<dyn Fn(i32, bool) -> i32> {
    Box::new(&|x, b| if b { x + 1 } else { x } )
}

我在这里错过了什么?这可能生锈吗? 我还想知道是否有比简单的高阶函数更具体的名称来描述我正在尝试做的事情,因为我正在努力寻找有关此类问题的资源。

更新:我意识到“currying in rust”是一个很好的搜索词。虽然这不是柯里化的示例,但它会使用相同的语言功能,并且会引导我找到 vallentin 给出的答案。

您正在尝试 return 对 conditional 函数中定义的闭包的加框引用。你不能这样做,因为闭包只在通话期间有效。相反,您可以 return 闭包本身,简而言之,只需删除 &,即将 &move |x, b| ... 变为 move |x, b| ....

fn conditional<T>(f: &'static (dyn Fn(T) -> T + 'static)) -> Box<dyn Fn(T, bool) -> T> {
    Box::new(move |x, b| if b { f(x) } else { x })
}

但是,编写您尝试的内容的更惯用的方法是使用泛型和闭包的类型参数。查看:

简而言之,你可以改写成这样:

fn conditional<F, T>(f: F) -> Box<dyn Fn(T, bool) -> T>
where
    F: Fn(T) -> T + 'static,
    T: 'static,
{
    Box::new(move |x, b| if b { f(x) } else { x })
}

其实你也可以不用盒子,使用impl Trait syntax

fn conditional<F, T>(f: F) -> impl Fn(T, bool) -> T
where
    F: Fn(T) -> T,
{
    move |x, b| if b { f(x) } else { x }
}

您也可以对参数使用 impl Trait 语法,链接显示了该语法,但我个人觉得在处理闭包时它很吵。

使用它可以归结为像这样简单的事情:

let f = conditional(move |x| x * 2);
println!("{}", f(2, false)); // Prints `2`
println!("{}", f(2, true));  // Prints `4`