Fn Traits
Rust 中的闭包通过三个 trait 来表示:Fn、FnMut 和 FnOnce。理解这些 trait 对于掌握闭包的行为和使用场景至关重要。
三个 Fn Traits
FnOnce - 只能调用一次
FnOnce 表示只能调用一次的闭包,因为它可能会消耗(move)捕获的值。
fn call_once<F>(f: F) -> String
where
F: FnOnce() -> String,
{
f() // 只能调用一次
}
fn main() {
let name = String::from("Rust");
// 这个闭包获取了 name 的所有权
let closure = move || {
format!("Hello, {}!", name) // name 被消耗
};
let result = call_once(closure);
println!("{}", result);
// closure 不能再次使用
// let result2 = call_once(closure); // 编译错误!
}
FnMut - 可以多次调用,可变借用
FnMut 表示可以多次调用的闭包,但可能会修改捕获的值。
fn call_multiple_mut<F>(mut f: F)
where
F: FnMut() -> i32,
{
println!("第一次调用: {}", f());
println!("第二次调用: {}", f());
println!("第三次调用: {}", f());
}
fn main() {
let mut counter = 0;
let mut increment = || {
counter += 1;
counter
};
call_multiple_mut(increment);
// counter 的值已经被修改
println!("最终计数: {}", counter);
}
Fn - 可以多次调用,不可变借用
Fn 表示可以多次调用的闭包,只对捕获的值进行不可变借用。
fn call_multiple<F>(f: F)
where
F: Fn(i32) -> i32,
{
println!("f(1) = {}", f(1));
println!("f(2) = {}", f(2));
println!("f(3) = {}", f(3));
}
fn main() {
let multiplier = 5;
let multiply_by_five = |x| x * multiplier;
call_multiple(multiply_by_five);
// multiplier 仍然可以使用
println!("multiplier = {}", multiplier);
}
Trait 层次结构
这三个 trait 形成了一个层次结构:
// 简化的 trait 定义
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
trait FnMut<Args>: FnOnce<Args> {
fn call_mut(&mut self, args: Args) -> Self::Output;
}
trait Fn<Args>: FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
}
自动实现关系
fn demonstrate_trait_hierarchy() {
// Fn 闭包可以用在任何需要 FnMut 或 FnOnce 的地方
let add_one = |x: i32| x + 1;
// 可以作为 Fn 使用
let fn_closure: &dyn Fn(i32) -> i32 = &add_one;
println!("Fn: {}", fn_closure(5));
// 也可以作为 FnMut 使用
let mut fn_mut_closure: &mut dyn FnMut(i32) -> i32 = &mut { add_one };
println!("FnMut: {}", fn_mut_closure(5));
// 还可以作为 FnOnce 使用
let fn_once_closure: Box<dyn FnOnce(i32) -> i32> = Box::new(add_one);
println!("FnOnce: {}", fn_once_closure(5));
}
捕获模式决定 Trait
闭包实现哪个 trait 取决于它如何捕获环境变量:
不捕获变量 - 实现 Fn
fn main() {
// 不捕获任何变量
let pure_function = |x: i32| x * 2;
// 实现了 Fn trait
call_fn(pure_function);
call_fn_mut(pure_function);
call_fn_once(pure_function);
}
fn call_fn<F: Fn(i32) -> i32>(f: F) {
println!("Fn: {}", f(5));
}
fn call_fn_mut<F: FnMut(i32) -> i32>(mut f: F) {
println!("FnMut: {}", f(5));
}
fn call_fn_once<F: FnOnce(i32) -> i32>(f: F) {
println!("FnOnce: {}", f(5));
}
不可变借用 - 实现 Fn
fn main() {
let multiplier = 3;
// 不可变借用 multiplier
let multiply = |x| x * multiplier;
// 可以多次调用
println!("第一次: {}", multiply(4));
println!("第二次: {}", multiply(5));
// multiplier 仍然可用
println!("multiplier: {}", multiplier);
}
可变借用 - 实现 FnMut
fn main() {
let mut sum = 0;
// 可变借用 sum
let mut accumulate = |x: i32| {
sum += x;
sum
};
println!("累加 5: {}", accumulate(5));
println!("累加 3: {}", accumulate(3));
// 注意:在闭包存在期间,sum 不能直接访问
}
获取所有权 - 实现 FnOnce
fn main() {
let data = vec![1, 2, 3, 4, 5];
// 获取 data 的所有权
let consume_data = move || {
let sum: i32 = data.iter().sum();
format!("数据总和: {}", sum)
};
println!("{}", consume_data());
// data 已经被移动,不能再使用
// println!("{:?}", data); // 编译错误!
}
实际应用场景
回调函数
struct EventSystem {
callbacks: Vec<Box<dyn Fn(&str)>>,
}
impl EventSystem {
fn new() -> Self {
EventSystem {
callbacks: Vec::new(),
}
}
fn add_listener<F>(&mut self, callback: F)
where
F: Fn(&str) + 'static,
{
self.callbacks.push(Box::new(callback));
}
fn emit_event(&self, event: &str) {
for callback in &self.callbacks {
callback(event);
}
}
}
fn main() {
let mut event_system = EventSystem::new();
// 添加不同类型的监听器
event_system.add_listener(|event| {
println!("日志: 收到事件 '{}'", event);
});
let prefix = "处理器";
event_system.add_listener(move |event| {
println!("{}: 处理事件 '{}'", prefix, event);
});
event_system.emit_event("用户登录");
}
状态机
struct StateMachine<S, F>
where
F: FnMut(&mut S, &str) -> S,
{
state: S,
transition: F,
}
impl<S, F> StateMachine<S, F>
where
F: FnMut(&mut S, &str) -> S,
{
fn new(initial_state: S, transition: F) -> Self {
StateMachine {
state: initial_state,
transition,
}
}
fn process_event(&mut self, event: &str) {
self.state = (self.transition)(&mut self.state, event);
}
fn current_state(&self) -> &S {
&self.state
}
}
#[derive(Debug, Clone)]
enum State {
Idle,
Processing,
Complete,
Error,
}
fn main() {
let mut machine = StateMachine::new(
State::Idle,
|current_state, event| {
match (current_state, event) {
(State::Idle, "start") => State::Processing,
(State::Processing, "finish") => State::Complete,
(State::Processing, "error") => State::Error,
(State::Error, "reset") => State::Idle,
(State::Complete, "reset") => State::Idle,
_ => current_state.clone(),
}
},
);
println!("初始状态: {:?}", machine.current_state());
machine.process_event("start");
println!("处理 'start': {:?}", machine.current_state());
machine.process_event("finish");
println!("处理 'finish': {:?}", machine.current_state());
}
函数组合
fn compose<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |x| g(f(x))
}
fn main() {
let add_one = |x: i32| x + 1;
let double = |x: i32| x * 2;
let square = |x: i32| x * x;
// 组合函数
let add_one_then_double = compose(add_one, double);
let complex_operation = compose(add_one_then_double, square);
println!("((5 + 1) * 2)² = {}", complex_operation(5));
}
性能优化
避免不必要的装箱
// 不好:总是使用 Box
fn bad_approach() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x * 2)
}
// 好:使用 impl Trait
fn good_approach() -> impl Fn(i32) -> i32 {
|x| x * 2
}
// 更好:泛型参数
fn best_approach<F>(f: F) -> F
where
F: Fn(i32) -> i32,
{
f
}
内联优化
#[inline]
fn apply_operation<F>(x: i32, op: F) -> i32
where
F: Fn(i32) -> i32,
{
op(x)
}
fn main() {
let double = |x| x * 2;
// 这个调用可能被完全内联
let result = apply_operation(5, double);
println!("结果: {}", result);
}
高级用法
闭包作为结构体字段
struct Calculator<F>
where
F: Fn(f64, f64) -> f64,
{
operation: F,
name: String,
}
impl<F> Calculator<F>
where
F: Fn(f64, f64) -> f64,
{
fn new(name: String, operation: F) -> Self {
Calculator { operation, name }
}
fn calculate(&self, a: f64, b: f64) -> f64 {
(self.operation)(a, b)
}
fn name(&self) -> &str {
&self.name
}
}
fn main() {
let adder = Calculator::new("加法器".to_string(), |a, b| a + b);
let multiplier = Calculator::new("乘法器".to_string(), |a, b| a * b);
println!("{}: 2.5 + 3.7 = {}", adder.name(), adder.calculate(2.5, 3.7));
println!("{}: 2.5 * 3.7 = {}", multiplier.name(), multiplier.calculate(2.5, 3.7));
}
动态闭包集合
type Operation = Box<dyn Fn(i32) -> i32>;
struct OperationChain {
operations: Vec<Operation>,
}
impl OperationChain {
fn new() -> Self {
OperationChain {
operations: Vec::new(),
}
}
fn add_operation<F>(&mut self, op: F)
where
F: Fn(i32) -> i32 + 'static,
{
self.operations.push(Box::new(op));
}
fn execute(&self, mut value: i32) -> i32 {
for operation in &self.operations {
value = operation(value);
}
value
}
}
fn main() {
let mut chain = OperationChain::new();
chain.add_operation(|x| x + 1);
chain.add_operation(|x| x * 2);
chain.add_operation(|x| x - 3);
let result = chain.execute(5);
println!("((5 + 1) * 2) - 3 = {}", result);
}
最佳实践
1. 选择合适的 Trait
// 如果闭包不需要修改捕获的值,使用 Fn
fn process_items<F>(items: &[i32], processor: F)
where
F: Fn(i32) -> String,
{
for item in items {
println!("{}", processor(*item));
}
}
// 如果需要修改状态,使用 FnMut
fn accumulate<F>(items: &[i32], mut accumulator: F) -> i32
where
F: FnMut(i32, i32) -> i32,
{
let mut result = 0;
for &item in items {
result = accumulator(result, item);
}
result
}
// 如果只调用一次,使用 FnOnce
fn execute_once<F>(task: F) -> String
where
F: FnOnce() -> String,
{
task()
}
2. 避免过度装箱
// 好的做法:使用泛型
fn good_map<T, U, F>(items: Vec<T>, f: F) -> Vec<U>
where
F: Fn(T) -> U,
{
items.into_iter().map(f).collect()
}
// 避免:不必要的装箱
fn avoid_boxing(items: Vec<i32>) -> Vec<Box<dyn Fn(i32) -> i32>> {
items.into_iter()
.map(|x| Box::new(move |y| x + y) as Box<dyn Fn(i32) -> i32>)
.collect()
}
理解 Fn traits 是掌握 Rust 闭包的关键,它们决定了闭包的行为和使用限制。下一节我们将学习闭包的高级应用。