如何将 Rust 函数作为参数传递?
Posted
技术标签:
【中文标题】如何将 Rust 函数作为参数传递?【英文标题】:How do you pass a Rust function as a parameter? 【发布时间】:2016-07-23 06:53:19 【问题描述】:我可以将函数作为参数传递吗?如果没有,有什么好的选择?
我尝试了一些不同的语法,但我没有找到正确的语法。我知道我可以做到:
fn example()
let fun: fn(value: i32) -> i32;
fun = fun_test;
fun(5i32);
fn fun_test(value: i32) -> i32
println!("", value);
value
但这并不是将函数作为参数传递给另一个函数:
fn fun_test(value: i32, (some_function_prototype)) -> i32
println!("", value);
value
【问题讨论】:
【参考方案1】:当然可以:
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,您必须考虑到 ownership and lifetime of the closure。
TL;DR;基本上有 3 种类型的闭包(可调用对象):
Fn
:不能修改捕获的对象。
FnMut
:它可以修改它捕获的对象。
FnOnce
:最受限制的。只能调用一次,因为调用时它会消耗自身及其捕获。
详情请见When does a closure implement Fn, FnMut and FnOnce?
如果您使用的是像闭包这样简单的指向函数的指针,那么捕获集是空的,并且您具有 Fn
风格。
如果你想做更多花哨的东西,那么你将不得不使用 lambda 函数。
在 Rust 中有正确的指向函数的指针,它们的工作方式与 C 中的一样。它们的类型例如是 fn(i32) -> i32
。 Fn(i32) -> i32
、FnMut(i32) -> i32
和 FnOnce(i32) -> i32
实际上是特征。指向函数的指针总是实现所有这三个,但 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;
//static dispatch
fun_test_impl(5, times2);
fun_test_impl(5, |x| 2*x);
fun_test_impl(5, |x| y*x);
//dynamic dispatch
fun_test_dyn(5, ×2);
fun_test_dyn(5, &|x| 2*x);
fun_test_dyn(5, &|x| y*x);
//C-like pointer to function
fun_test_ptr(5, times2);
fun_test_ptr(5, |x| 2*x); //ok: empty capture set
fun_test_ptr(5, |x| y*x); //error: expected fn pointer, found closure
【讨论】:
使用Fn*
是特征,所以通常的 <T: Trait>
与 (t: &T)
都适用。非泛型解决方案的主要限制是它必须与引用一起使用。所以如果你想要FnOnce
,它应该作为副本传递,你必须使用通用样式。
请注意,使用泛型而不是 trait 对象更为惯用(即 <F: Fn..>
而不是 (f: &Fn...)
。这是有原因的 - 泛型将导致静态调度,而 trait 对象需要动态调度。
有趣的是,从 interface(调用者)的角度来看,FnOnce
实际上是最通用的特征——它接受所有闭包,无论它们是读取、修改还是取得所有权的捕获状态。 FnMut
更具限制性,它不接受获取捕获对象所有权的闭包(但它仍然允许修改状态)。 Fn
是最严格的,因为它不接受修改其捕获状态的闭包。因此,要求&Fn
对funTest
调用者的限制最大,而对如何在其中调用f
的限制最小。【参考方案2】:
Fn
、FnMut
和 FnOnce
,在另一个答案中概述,是 closure 类型。超出其范围的函数类型。
除了传递闭包之外,Rust 还支持传递简单(非闭包)函数,如下所示:
fn times2(value: i32) -> i32
2 * value
fn fun_test(value: i32, f: fn(i32) -> i32) -> i32
println!("", f (value));
value
fn main()
fun_test (2, times2);
fn(i32) -> i32
这里是function pointer type。
如果您不需要一个成熟的闭包,那么使用函数类型通常会更简单,因为它不必处理那些闭包生命周期的细节。
【讨论】:
@IvanTemchenko 也许吧?这里有一些代码供你使用:play.rust-lang.org/… 这不是我的意思 =) 找到了返回 dyn 闭包的解决方法,该闭包捕获了自己的状态,所以我不需要传递实例引用...以上是关于如何将 Rust 函数作为参数传递?的主要内容,如果未能解决你的问题,请参考以下文章