• Rust中的高阶函数用法
  • 发布于 2个月前
  • 173 热度
    0 评论
高阶函数是其参数或返回值本身就是函数的函数。换句话说,如果一种语言支持高阶函数,那么我们就说这些函数是一等公民,也就是说它们是值。在本文中,我们将研究Rust如何支持高阶函数,以及如何定义它们。

Rust中的函数
我们可以通过fn关键字在Rust中定义函数。像往常一样,定义一个函数,我们必须指定它的名称,参数和返回值的类型:
fn plus_one(n: i32) -> i32 {
    n + 1
}
return关键字是可选的。如果我们不指定它,函数的最后一条语句被认为是返回语句。如前所述,Rust中的函数是一等公民。因此,我们可以将它们存储在一个变量中。一旦它被存储在变量中,我们可以像往常一样调用它:
fn main() {
    let add_one = plus_one;

    println!("{}", add_one(1));
}
函数作为参数
我们演示了如何定义函数并将其存储在变量中。现在,让我们看看如何将一个函数作为参数传递给另一个函数。
首先,我们定义一个高阶函数:
fn binary_operator(n: i32, m: i32, op: F) -> i32 
where F: Fn(i32, i32) -> i32 {
    op(n, m)
}
binary_operator输入两个数字n和m,以及一个函数op。它对n和m应用op,返回结果。

注意op参数的类型。它是一个泛型类型F,在binary_operator的where子句中进行了细化。特别地,我们将其定义为具有两个数值形参(i32, i32)的函数Fn,返回形参i32。Fn在这里表示一个函数指针,即存储函数的内存地址。

实名函数
将函数作为参数传递的最简单方法是使用它的名称(named function):
add(n: i32, m: i32) -> i32 {
    n + m
}

fn binary_operator(n: i32, m: i32, op: F) -> i32 
where F: Fn(i32, i32) -> i32 {
    op(n, m)
}

fn main() {
    println!("{}", binary_operator(5, 6, add));
}
在上面的例子中,我们首先定义一个函数add来相加两个数字,并将其用作binary_operator的参数。结果打印11,如预期。

匿名函数
有时候没有必要给函数命名。例如,我们可能想要动态地定义一个函数,只在一个地方使用。这就是匿名函数发挥作用的地方:
fn main() {
    println!("{}", binary_operator(5, 6, |a: i32, b: i32| a - b));
}
在上面的例子中,我们在调用binary_operator时直接定义了一个匿名函数。||之间是参数列表,然后是函数体本身。匿名函数是一个非常强大的工具,因为在Rust中,它们可以“捕获”上下文环境。在这种情况下,函数也称为闭包。正如预期的那样,上面的代码段编译并输出-1。

函数作为返回值
正如我们前面提到的,在Rust中一个函数也可以返回另一个函数。由于Rust中内存管理的结果,这有点复杂,我们很快就会看到。在下面的例子中,我们将修改上面定义的binary_operator,重新定义一个unapplied_binary_operator函数:
fn unapplied_binary_operator<'a, F>(n:&'a i32, m:&'a i32, op:&'a F) -> Box<dyn Fn() -> i32 + 'a>
where F: Fn(i32, i32) -> i32 {
    Box::new(move || op(*n, *m))
}
unapplied_binary_operator的定义现在看起来要复杂得多。返回函数的主要问题是定义函数生命周期的长度。在Rust中,生命周期是借用检查器用来确保所有借用都是有效的。在上面的例子中,我们定义了一个生命周期'a和泛型的F类型。然后,我们将'a与三个参数以及函数的返回值关联起来。基本上,我们告诉借用检查器,只要三个参数(n、m和op)存在,就考虑unapplied_binary_operator返回的函数的生命期。

此外,Rust中的生命周期只存在于引用中。因此,我们必须将参数和返回值分别用&和Box转换为引用。一般来说,Box<dyn Fn()>表示实现Fn特征的装箱值。

上述实现中另一个有趣的地方是move的使用。该关键字表示所有捕获(即对封闭环境的所有引用)都是按值发生的。否则,只要返回的匿名函数存在,任何引用捕获都将被删除,从而使闭包保留无效引用。换句话说,通过move,闭包获得了它使用的变量的所有权。

unapplied_binary_operator返回一个不带参数的函数,返回op应用于n和m的结果。假设我们现在使用引用,我们必须使用借用来调用它:
fn main() {
    let n = 5;
    let m = 6;
    println!("{}", unapplied_binary_operator(&n, &m, &add)());
}
注意我们如何借用n, m,并使用&。最后,由于unapplied_binary_operator返回一个不带参数的函数,我们可以使用空括号调用它。正如预期的那样,上面的代码片段将打印11。

总结
在本文中,我们快速研究了Rust中的高阶函数。我们演示了如何定义一个简单的函数。然后,我们探索了将函数作为参数传递的方法。最后,我们研究了返回一个函数有多复杂,简要地提到了Rust的一些关键特性,比如借用和生命周期。

高阶函数是Rust和许多其他编程语言中的一个关键特性;高阶函数是函数式编程的一个基本概念。我们可以使用高阶函数来编写更简洁、更易于长期维护的代码。
用户评论