当然可以:
fn fun_test(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn times2(value: i32) -> i32 {
2 * value
}
fn main() {
fun_test(5, ×2);
}
由于这是 Rust,你必须考虑到 闭包的所有权和生命周期。
简而言之,基本上有三种类型的闭包(可调用对象):
Fn
:它不能修改捕获的对象。
FnMut
:它可以修改捕获的对象。
FnOnce
:最受限制的。只能被调用一次,因为当它被调用时,它会消耗自己和它的捕获物。
请参阅 闭包何时实现 Fn、FnMut 和 FnOnce? 以获取更多详细信息。
如果你使用像指向函数的简单指针这样的闭包,则捕获设置为空,且你具有 Fn
类型。
如果你想要做更高级的操作,则必须使用 lambda 函数。
在Rust中,有适当的函数指针,与C语言中的指针类似。它们的类型例如为
fn(i32) -> i32
。
Fn(i32) -> i32
,
FnMut(i32) -> i32
和
FnOnce(i32) -> i32
实际上是特征(traits)。函数指针始终实现这三个特征,但是Rust还具有闭包,可以将其转换为指针(取决于捕获集是否为空),但它们确实实现了其中一些特征。
因此,例如,上面的示例可以扩展:
fn fun_test_impl(value: i32, f: impl Fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn fun_test_dyn(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn fun_test_ptr(value: i32, f: fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn times2(value: i32) -> i32 {
2 * value
}
fn main() {
let y = 2;
fun_test_impl(5, times2);
fun_test_impl(5, |x| 2*x);
fun_test_impl(5, |x| y*x);
fun_test_dyn(5, ×2);
fun_test_dyn(5, &|x| 2*x);
fun_test_dyn(5, &|x| y*x);
fun_test_ptr(5, times2);
fun_test_ptr(5, |x| 2*x);
fun_test_ptr(5, |x| y*x);
}
Fn*
是特质(trait),因此通常的<T: Trait>
vs(t: &T)
适用。非泛型解决方案的主要限制是必须使用引用。因此,如果您想要作为副本传递的FnOnce
,则必须使用泛型风格。 - rodrigo<F: Fn..>
而不是(f: &Fn...)
)。这是有原因的 - 泛型将导致静态分派,而特质对象需要动态分派。 - Vladimir MatveevFnOnce
实际上是最通用的trait-它接受所有闭包,无论它们是否读取、修改或获取捕获的状态。FnMut
更为严格,它不接受获取已捕获对象所有权的闭包(但仍允许修改状态)。Fn
是最为严格的,因为它不接受修改其捕获状态的闭包。因此,要求使用&Fn
会对funTest
调用者施加最大的限制,同时对f
在其中如何被调用提供最小的限制。 - user4815162342