以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正
在之前闭包
的文章中,我们可以知道,在Rust中函数也可以像在Python、Nodejs一样作为参数传入到另一个函数中,具体使用时主要有两种方法来实现
闭包作为参数
在之前的文章中讲到闭包对捕获的变量所有权有三种特性:Fn
、FnMut
和FnOnce
,其分别表示了闭包在捕获环境时的不同方式。
Fn闭包作为参数
Fn
:闭包通过不可变借用捕获环境中的变量,可以在多次调用中复用,不会改变捕获的变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| fn apply_fn<F>(f: F, value: i32) -> i32 where F: Fn(i32) -> i32, { f(value) }
fn main() { let result1 = apply_fn(|x| x * 2, 5); println!("Result1: {}", result1);
let offset = 3; let result2 = apply_fn(|x| x + offset, 5); println!("Result2: {}", result2); println!("factor: {}", factor); }
|
FnMut闭包作为参数
FnMut
:闭包通过可变借用捕获环境中的变量,可以修改捕获的变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| fn apply_fn_mut<F>(mut f: F, value: i32) -> i32 where F: FnMut(i32) -> i32, { f(value) }
fn main() { let mut factor = 2; let result1 = apply_fn_mut( |x| { factor *= 2; x * factor }, 5, ); println!("Result1 (FnMut): {}", result1); println!("factor: {}", factor); }
|
运行上面的代码可以发现,变量factor
的值发生了改变
FnOnce闭包作为参数
FnOnce
:闭包通过值捕获环境中的变量,消耗捕获的变量,这种闭包只能调用一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| fn apply_fn_once<F>(f: F, value: i32) -> i32 where F: FnOnce(i32) -> i32, { f(value) }
fn main() { let factor = String::from("2"); let result2 = apply_fn_once(move |x| x * factor.parse::<i32>().unwrap(), 5); println!("Result2 (FnOnce): {}", result2); println!("{}", factor); }
|
函数指针作为参数
通过以上的例子可以看出,使用闭包在非常方便的将函数作为参数传入到其他函数中(闭包也是一种函数),但使用闭包不容易编写逻辑性较为复杂的代码,否则可能会造成代码难以阅读,这时我们可以使用函数指针
函数指针作为参数
下面是一个最简单的函数指针作为参数的例子
1 2 3 4 5 6 7 8 9 10 11 12 13
| fn apply(f: fn(i32) -> i32, x: i32) -> i32 { f(x) }
fn double(x: i32) -> i32 { x * 2 }
fn main() { let result = apply(double, 5); println!("Result: {}", result); }
|
多个函数指针参数
1 2 3 4 5 6 7 8 9 10 11
| fn process(input: i32, f1: fn(i32) -> i32, f2: fn(i32) -> i32) -> i32 { f2(f1(input)) }
fn double(x: i32) -> i32 { x * 2 } fn square(x: i32) -> i32 { x * x }
fn main() { let result = process(3, double, square); println!("Result: {}", result); }
|
函数指针作为返回值
1 2 3 4 5 6 7 8 9 10 11 12
| fn get_operation(op: &str) -> fn(i32, i32) -> i32 { match op { "add" => |x, y| x + y, "multiply" => |x, y| x * y, _ => |x, _| x, } }
fn main() { let add_op = get_operation("add"); println!("Result: {}", add_op(5, 3)); }
|
特征对象
结合之前Box<T>
智能指针,可以更灵活的使用函数指针,比如下面的例子
1 2 3 4 5 6 7 8 9 10 11 12 13
| fn main() { let functions: Vec<Box<dyn Fn(i32) -> i32>> = vec![ Box::new(|x| x + 1), Box::new(|x| x * 2), Box::new(|x| x * x), ];
for f in functions.iter() { println!("Result: {}", f(5)); } }
|
以上只是一个非常基础的举例,事实上还有很多巧妙的用法,这里就不赘述了
总结
使用函数作为参数可以使程序更灵活、强大,可以提高代码的复用性和灵活性和实现回调机制等等优点,但在使用中也需要结合特定的场景来选择。比如需要考虑环境变量时,可能使用闭包更好用,但需要实现较为复杂的函数功能时,可能函数指针更加的方便,具体需要根据我们在实际编程中的使用场景来确定