函数指针fn(x)->y
函数的类型是 fn (使用小写的 “f” )以免与 Fn 闭包 trait 相混淆。fn 被称为 函数指针(function pointer)。
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
}
编译
cargo run
Blocking waiting for file lock on build directory
Compiling blog v0.1.0 (/home/wangji/installer/rust/bobo/smartPtr)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 9.69s
Running `target/debug/blog`
The answer is: 12
三种闭包类型:Fn,FnMut,FnOnce
Fn:以不可变形式捕获外部变量
FnMut:以可变的形式捕获外部变量
FnOnce:以转移所有权的方式捕获外部变量
闭包作为参数的好处:既可以传入参数,也可以传入函数指针
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice<T>(f: T, arg: i32) -> i32
where
T: Fn(i32) -> i32,
{
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
}
#![allow(unused)]
fn main() {
let list_of_numbers = vec![1, 2, 3];
let list_of_strings: Vec<String> = list_of_numbers.iter().map(|i| i.to_string()).collect();
//使用函数指针替代
// 看ToString::to_string)源码可知,任何实现Display特征的类型,都会实现ToString特征(也就可以调用to_string()方法)
let list_of_strings: Vec<String> = list_of_numbers.iter().map(ToString::to_string).collect();
}
元组结构体和元组结构体成员
#![allow(unused)]
fn main() {
enum Status {
// 这些项使用 () 作为初始化语法,这看起来就像函数调用,
//同时它们确实被实现为返回由参数构造的实例的函数。它们也被称为实现了闭包 trait 的函数指针,
Value(u32),
Stop,
}
// Status::Value当作一个函数指针,入参就是u32类型,返回值是Status类型
let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
// 使用闭包初始化 `Status::Value`
let list_of_statuses: Vec<Status> = (0u32..20)
.map(|x| Status::Value(x)) // 闭包
.collect();
}
返回一个闭包
可以的
#![allow(unused)]
fn main() {}
fn returns_closure() -> impl Fn(i32) -> i32 {
|x| x + 1
}
错误代码
- 由于闭包捕获 x,并且 x 可能会在不同的分支中被捕获为不同的值,Rust 必须确保闭包的生命周期与 x 的生命周期一致,而这需要更精确的类型推断。
#![allow(unused)]
fn main() {}
fn returns_closure(x: i32) -> impl Fn(i32) -> i32 {
if x > 0 {
move |y| x + y
} else {
move |y| x - y
}
}
解决办法:使用特征对象进行动态派遣
#![allow(unused)]
fn main() {}
fn returns_closure(x: i32) -> Box<dyn Fn(i32) -> i32> {
if x > 0 {
Box::new(move |y| x + y)
} else {
Box::new(move |y| x - y)
}
}
动态派遣 (dyn Fn(i32) -> i32):
- 使用 dyn Fn(i32) -> i32 是 Rust 中的 动态特征对象(动态派遣)(trait object),它允许在运行时确定闭包的类型,而不需要在编译时确定所有具体的类型。
- 在你的原始代码中,impl Fn(i32) -> i32 是一个 静态派遣 类型,它要求编译器在编译时推断出闭包的具体类型。由于你在 if 和 else 分支中返回不同的闭包,这会使得 Rust 无法统一推断返回的闭包类型,因此需要用动态特征对象来解决。
在原始的代码中,当使用 impl Fn(i32) -> i32 时,Rust 需要在编译时确定返回的闭包类型。但由于你在 if 和 else 分支中创建了两种不同的闭包,它们的类型会有所不同,导致 Rust 无法推导出统一的返回类型。
通过使用 Box<dyn Fn(i32) -> i32>,你将返回值的类型改成了 动态特征对象,这意味着 Rust 在编译时不再需要知道闭包的具体类型,而是通过运行时的动态派遣来决定如何调用闭包。此外,闭包被存储在堆上(通过 Box),因此它们可以拥有不同的大小,而 Rust 不需要在栈上存储这些不同大小的闭包。