我正在尝试创建一个函数来执行以下操作:
接受 fn(T) -> T
形式的闭包 f
返回 fn(T, bool) -> T
形式的闭包,它根据 bool 参数有条件地执行 f
。
我来自 haskell-ish 背景,在 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
我在想'当前函数拥有的数据'要么是我定义的闭包,要么是我移动的f
,但我想不通围绕它们如何组合在一起。
作为冒烟检查,我确保我能够装箱我在函数体中定义的更简单的闭包,并编译以下内容:
fn conditional_increment() -> Box<dyn Fn(i32, bool) -> i32> {
Box::new(&|x, b| if b { x + 1 } else { x } )
}
我在这里错过了什么?这可能使用rust 吗? 我还想知道是否有比简单的高阶函数更具体的名称来表示我正在尝试做的事情,因为我正在努力寻找有关此类问题的资源。
更新:我意识到“currying in rust”是一个很好的搜索词。虽然这不是柯里化(Currying)的示例,但它会使用相同的语言功能,并且会引导我找到 vallentin 给出的答案。
最佳答案
您正在尝试返回对 conditional
函数中定义的闭包的装箱引用。你不能这样做,因为闭包只在通话期间有效。相反,您可以返回闭包本身,简而言之,只需删除 &
,即 turn &move |x, b| ...
进入 移动 |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 })
}
但是,编写您尝试的内容的更惯用的方法是使用泛型和闭包的类型参数。查看:
- Rust By Example - (Closures) As input parameters
- Rust By Example - (Closures) As output parameters .
简而言之,您可以将其重写为:
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`
关于rust - 修改并返回闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65527921/