5分钟速读之Rust权威指南(四十二)高级函数

510 阅读2分钟

最近几节我们都在聊一些高级特性,这一节来看函数之前没提到过的一些特性:函数指针、高阶函数

函数指针

在讲到闭包时候,我们将闭包可以作为函数参数传递,其实函数也可以作为参数,在参数位置使用fn来描述函数类型:

fn add(a: i32, b: i32) -> i32 {
  a + b
}

// 使用fn类型来描述函数
type Adder = fn(i32, i32) -> i32;

// 描述参数类型
fn run(f1: Adder, f2: Adder) -> i32 {
  f1(1, 2) + f2(3, 4)
}

// 将add函数的指针传入run函数
println!("{}", run(add, add));
// 10

函数指针和闭包trait都可以兼容闭包和普通函数,看下面这个复杂的例子:

// 函数
fn sub(a: i32, b: i32) -> i32 {
  a - b
}

// 闭包
let add = |a: i32, b: i32| -> i32 {
  a - b
};

// f1是闭包trait,f2是函数指针
fn run(f1: impl Fn(i32, i32) -> i32, f2: fn(i32, i32) -> i32) -> i32 {
  f1(1, 2) + f2(3, 4)
}

// f1是函数指针,f2是闭包trait
fn run2(f1: fn(i32, i32) -> i32, f2: impl Fn(i32, i32) -> i32) -> i32 {
  f1(1, 2) + f2(3, 4)
}

println!("{}", run(add, sub)); // -2
println!("{}", run2(add, sub)); // -2

下面是一个既可以使用闭包也可以使用命名函数的更具体的例子:

// 使用闭包的情况:
let list_nums = vec![1, 2, 3];
let list_str: Vec<String> = list_nums
  .iter()
  .map(|n| n.to_string())
  .collect();

println!("{:?}", list_str);
// ["1", "2", "3"]

// 使用函数的情况:
let list_str: Vec<String> = list_nums
	.iter()
  // 这里可以思考一下,ToString是一个trait,这里使用了前面“高级trait”一节中提到的完全限定语法
	.map(ToString::to_string)
	.collect();

println!("{:?}", list_str);
// ["1", "2", "3"]

枚举也可以作为函数:

#[derive(Debug)]
enum Status {
  Value(u32),
  Stop,
}
let list_status: Vec<Status> = (1..=3).map(Status::Value)
  .collect();
println!("{:?}", list_status);
// [Value(1), Value(2), Value(3)]

这里是一个高阶函数,将函数作为参数,返回一个闭包:

fn add(f: fn(i32) -> i32) -> impl Fn(i32) -> i32 {
  // 这里需要move的原因在于借用检查器会认为当add执行完成后f会销毁,
  // 而返回的闭包生命周期比较长,这里闭包用使用了add函数中的引用
  // 所以需要把f的所有权转移到闭包中用来让借用检查器检查通过
  return move |p: i32| {
    return f(p) + 1
  }
}

fn add_one(x: i32) -> i32 {
  x + 1
}

let run = add(add_one);
println!("{}", run(1));

还有种有意思的操作就是使用Box<T>来返回闭包:

fn return_closure() -> Box<dyn Fn(i32) -> i32> {
  Box::new(|x| x + 1)
}
// 将闭包从Box中解引用获取,然后执行
let x = (*return_closure())(1);
println!("{}", x);
// 2

封面图:跟着Tina画美国

关注「码生笔谈」公众号,阅读更多最新章节