如何将 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, &times2);

由于这是 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) -> i32Fn(i32) -> i32FnMut(i32) -> i32FnOnce(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, &times2);
    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

【讨论】:

使用 与不使用(.., f: &Fn...)这两个作品是有区别的,有些细节我需要知道吗? @AngelAngel:嗯,Fn* 是特征,所以通常的 <T: Trait>(t: &T) 都适用。非泛型解决方案的主要限制是它必须与引用一起使用。所以如果你想要FnOnce,它应该作为副本传递,你必须使用通用样式。 请注意,使用泛型而不是 trait 对象更为惯用(即 <F: Fn..> 而不是 (f: &Fn...)。这是有原因的 - 泛型将导致静态调度,而 trait 对象需要动态调度。 有趣的是,从 interface(调用者)的角度来看,FnOnce 实际上是最通用的特征——它接受所有闭包,无论它们是读取、修改还是取得所有权的捕获状态。 FnMut 更具限制性,它不接受获取捕获对象所有权的闭包(但它仍然允许修改状态)。 Fn 是最严格的,因为它不接受修改其捕获状态的闭包。因此,要求&FnfunTest 调用者的限制最大,而对如何在其中调用f 的限制最小。【参考方案2】:

FnFnMutFnOnce,在另一个答案中概述,是 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 函数作为参数传递?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用字符串作为参数从 Go 调用 Rust 函数?

在 Rust 中将结构作为参数传递

如何将列表的所有元素作为函数的参数传递而不将列表作为参数传递

如何将函数作为参数传递,带参数? [复制]

使用rust-protobuf时我作为参数传递什么?

如何将构造函数(可变参数)作为模板参数传递?