Rust: Rust 闭包(Closures):匿名函数捕获环境

最后更新:2026-08-26

闭包(closure)是可以捕获周围环境变量的匿名函数。它像函数一样被调用,但能"记住"定义时所在作用域的变量。

如果说普通函数是"外卖菜单上的固定套餐"(食材从参数来),那闭包就是"你自己冰箱里的菜"(食材从环境来)。闭包可以访问定义时周围的变量——就像从冰箱里拿食材直接下厨。


1. 你将学到


2. 概念图解

以下 Mermaid 图展示闭包三种捕获方式(Fn / FnMut / FnOnce)的关系与区别:

100%
graph TB
    A["闭包 Closure"] --> B["Fn<br/>不可变借用 &T"]
    A --> C["FnMut<br/>可变借用 &mut T"]
    A --> D["FnOnce<br/>消费所有权 T"]

    B --> E["let c = || println!(&#34;{ }&#34;, x);"]
    B --> F["可多次调用"]
    B --> G["环境变量只读不可变"]

    C --> H["let mut c = || x += 1;"]
    C --> I["可多次调用"]
    C --> J["可修改环境变量"]

    D --> K["let c = || drop(x);"]
    D --> L["只能调用一次"]
    D --> M["所有权已移入闭包"]

    B -.-> |自动推断| N["编译器根据闭包体<br/>选择最低要求的 trait"]
    C -.-> N
    D -.-> N
    N --> O["move 关键字强制 FnOnce"]

3. 外卖评分系统的故事

(1) 痛苦:为每种评分规则写单独的函数

Mia (Mia) 在开发一个外卖评分系统。用户可以对店铺评分,但评分规则各不相同:

最开始她为每种规则写一个函数:

RUST
struct Review {
    store: String,
    rating: u32,
}

fn normal_score(r: &Review) -> u32 {
    r.rating
}

fn blogger_score(r: &Review) -> u32 {
    r.rating * 2
}

fn inspector_score(r: &Review) -> u32 {
    if r.rating < 3 { 0 } else { r.rating }
}

fn main() {
    let reviews = vec![
        Review { store: "Pizzaplace".into(), rating: 4 },
        Review { store: "BurgerKing".into(), rating: 2 },
        Review { store: "SushiBar".into(), rating: 5 },
    ];

    for r in &reviews {
        println!("{}: normal={}, blogger={}, inspector={}",
            r.store, normal_score(r), blogger_score(r), inspector_score(r));
    }
}

三个函数除了核心逻辑不同,函数签名完全一样。如果产品经理说"再加一个超级会员评分规则"又要多写一个函数。而且这些评分规则不能"动态生成"——比如"评分 × 系数"中的系数必须是写死在函数里的。

(2) 更复杂的需求:评分系数动态变化

产品经理说:评分系数每天变化(根据促销活动),而且用户可以选择不同的评分策略(打分、加权、惩罚低分)。用普通函数做不到——函数签名在编译时固定,无法捕获外部的动态系数。

RUST
// 想用函数,但系数是动态的
let weight = 1.5;  // 今天的权重系数
// fn weighted_score(r: &Review) -> u32 {
//     (r.rating as f64 * weight) as u32  // ❌ 编译错误:weight 不在函数作用域内
// }

(3) Rust 闭包的方案

RUST
struct Review {
    store: String,
    rating: u32,
}

fn main() {
    let reviews = vec![
        Review { store: "Pizzaplace".into(), rating: 4 },
        Review { store: "BurgerKing".into(), rating: 2 },
        Review { store: "SushiBar".into(), rating: 5 },
    ];

    // Closure 1: normal score (no capture)
    let normal = |r: &Review| r.rating;

    // Closure 2: weighted score (captures `weight` from environment)
    let weight: f64 = 1.5;
    let weighted = |r: &Review| (r.rating as f64 * weight) as u32;

    // Closure 3: inspector (captures `threshold` from environment)
    let threshold = 3;
    let inspector = |r: &Review| if r.rating < threshold { 0 } else { r.rating };

    for r in &reviews {
        println!("{}: normal={}, weighted={}, inspector={}",
            r.store, normal(r), weighted(r), inspector(r));
    }
}

输出:

TEXT 📖 仅展示
Pizzaplace: normal=4, weighted=6, inspector=4
BurgerKing: normal=2, weighted=3, inspector=0
SushiBar: normal=5, weighted=7, inspector=5

闭包 |r| r.rating * weight 捕获了外部变量 weight——这是普通函数做不到的。weight 和 threshold 可以动态变化(比如来自用户输入或配置文件),闭包自动捕获当前值。这就是闭包的核心价值:一个可以从周围环境"偷"变量来用的函数。


4. 核心概念

(1) 闭包体系总览

100%
graph TB
    A[Rust Closures] --> B[Syntax]
    A --> C[Capture Modes]
    A --> D[move Keyword]
    A --> E[As Parameters]
    A --> F[With Iterators]

    B --> B1["|param1, param2| expr"]
    B --> B2["|param| { multiple; statements; }"]

    C --> C1["Fn: immutable borrow (&T)"]
    C --> C2["FnMut: mutable borrow (&mut T)"]
    C --> C3["FnOnce: ownership (T)"]

    D --> D1["let c = move || x;"]
    D --> D2["Forces ownership transfer"]

    E --> E1["fn pointer: fn(T) -> U"]
    E --> E2["Fn trait: impl Fn(T) -> U"]
    E --> E3["FnMut / FnOnce trait bounds"]

    F --> F1["iter().map(|x| x + 1)"]
    F --> F2["iter().filter(|x| x > 0)"]
    F --> F3["Chained: map().filter().collect()"]

(2) 三种捕获模式对比

Trait 捕获方式 调用次数 是否可修改环境 是否可以多次调用
FnOnce 消费所有权(move) 一次 可消费环境变量 否(所有权已转移)
FnMut 可变借用 多次 可以修改环境变量 是
Fn 不可变借用 多次 不能修改环境变量 是

(3) 函数指针 fn vs 闭包 Fn

特性 函数指针 fn 闭包 Fn trait
是否捕获环境 否——只能使用参数 是——可以捕获环境变量
能否作为参数 能 能(更通用)
能否接受闭包 不能 能
类型大小 fn(T) -> U(指针大小) 不同闭包有不同大小
性能 确定 编译器可内联
语法 fn foo(x: i32) -> i32 |x| x + 1

(4) 闭包捕获方式对比

捕获方式 语法示例 所有权影响 闭包可调用次数 适用场景
不可变借用 |x| x + var 借用 &T 多次 只读访问环境变量
可变借用 |x| { var += 1; ... } 借用 &mut T 多次(独占) 需修改环境变量
移动所有权 move |x| x + var 获取 T 仅一次(若消耗) 闭包需比引用活得更久
无捕获 |x| x + 1 无 多次 等价于函数指针

5. 闭包示例

▶ 示例 1:闭包基础语法——外卖评分(难度 ⭐)

RUST
// ============================================
// Closure basics: syntax, type inference, calling
// ============================================

struct Review {
    store: String,
    rating: u32,
}

fn main() {
    let reviews = vec![
        Review { store: String::from("Pizzaplace"), rating: 4 },
        Review { store: String::from("BurgerKing"), rating: 2 },
        Review { store: String::from("SushiBar"), rating: 5 },
        Review { store: String::from("NoodleHouse"), rating: 3 },
    ];

    // --- Syntax variation 1: single expression ---
    let double = |r: &Review| r.rating * 2;
    // --- Syntax variation 2: block body ---
    let triple = |r: &Review| {
        let result = r.rating * 3;
        println!("    (triple: {} -> {})", r.rating, result);
        result
    };
    // --- Syntax variation 3: type inference ---
    // Rust infers parameter and return types from usage
    let half = |r| r.rating / 2;  // type inferred as &Review -> u32

    println!("--- Rating Calculations ---");
    for r in &reviews {
        println!("{} (rating: {}):", r.store, r.rating);
        println!("  double={}, half={}", double(r), half(r));
        let _ = triple(r);
    }

    // --- Closures that capture variables ---
    let min_rating = 3;
    let min_rating2 = 4;

    // Capture `min_rating` from the surrounding scope
    let is_pass = |r: &Review| r.rating >= min_rating;
    let is_good = |r: &Review| r.rating >= min_rating2;

    println!("\n--- Pass Check (min: {}) ---", min_rating);
    for r in &reviews {
        println!("{}: {}", r.store, if is_pass(r) { "PASS" } else { "FAIL" });
    }

    println!("\n--- Good Check (min: {}) ---", min_rating2);
    for r in &reviews {
        println!("{}: {}", r.store, if is_good(r) { "GOOD" } else { "OK" });
    }

    // --- Using closures as function arguments ---
    fn check_reviews(reviews: &[Review], label: &str, scorer: impl Fn(&Review) -> u32) {
        print!("{}: ", label);
        for r in reviews {
            print!("{}->{} ", r.store, scorer(r));
        }
        println!();
    }

    let weight = 2;
    let weighted_scorer = |r: &Review| r.rating * weight;

    check_reviews(&reviews, "Weighted(×2)", weighted_scorer);
    check_reviews(&reviews, "Normal", |r| r.rating);
    check_reviews(&reviews, "Bonus", |r| if r.rating >= 4 { r.rating + 1 } else { r.rating });
}

输出:

TEXT 📖 仅展示
--- Rating Calculations ---
Pizzaplace (rating: 4):
  double=8, half=2
    (triple: 4 -> 12)
BurgerKing (rating: 2):
  double=4, half=1
    (triple: 2 -> 6)
SushiBar (rating: 5):
  double=10, half=2
    (triple: 5 -> 15)
NoodleHouse (rating: 3):
  double=6, half=1
    (triple: 3 -> 9)

--- Pass Check (min: 3) ---
Pizzaplace: PASS
BurgerKing: FAIL
SushiBar: PASS
NoodleHouse: PASS

--- Good Check (min: 4) ---
Pizzaplace: GOOD
BurgerKing: OK
SushiBar: GOOD
NoodleHouse: OK

--- Weighted(×2) ---
Pizzaplace->8 BurgerKing->4 SushiBar->10 NoodleHouse->6
Normal: Pizzaplace->4 BurgerKing->2 SushiBar->5 NoodleHouse->3
Bonus: Pizzaplace->5 BurgerKing->2 SushiBar->6 NoodleHouse->3

闭包语法有三种风格:单表达式 |x| expr、块体 |x| { stmt; expr }、类型推断 |x| x+1(类型从上下文推断)。捕获环境变量是闭包的独特能力:is_pass 捕获了 min_rating,weighted_scorer 捕获了 weight。check_reviews 函数接受 impl Fn(&Review) -> u32——任何实现了 Fn trait 的闭包都可以传入。


▶ 示例 2:捕获模式——Fn / FnMut / FnOnce(难度 ⭐⭐)

RUST
// ============================================
// Three capture modes: Fn, FnMut, FnOnce
// ============================================

fn main() {
    println!("=== Fn: Immutable Borrow ===");
    let scores = vec![10, 20, 30];
    // Fn closure: only reads captured variables (immutable borrow)
    let print_scores = || {
        println!("Scores: {:?}", scores);  // &Vec<i32>
    };
    print_scores();  // Can be called multiple times
    print_scores();
    println!("Still accessible: {:?}", scores);  // scores not moved

    println!("\n=== FnMut: Mutable Borrow ===");
    let mut counters = vec![0, 0, 0];
    // FnMut closure: can mutate captured variables
    let mut increment = || {
        for c in &mut counters {
            *c += 1;
        }
    };
    increment();  // Can be called multiple times
    increment();
    increment();
    println!("Counters: {:?}", counters);  // [3, 3, 3]

    println!("\n=== FnOnce: Ownership Consumed ===");
    let name = String::from("Pizzaplace");
    // FnOnce closure: consumes the captured variable
    let consume = || {
        println!("Consuming: {}", name);
        drop(name);  // Explicitly drop (consumes ownership)
    };
    consume();
    // consume();  // ❌ Compile error: closure can only be called once
    // println!("{}", name);  // ❌ Compile error: name was moved

    println!("\n=== FnOnce with move ===");
    let data = vec![1, 2, 3, 4, 5];
    // `move` forces the closure to take ownership of `data`
    let compute = move || {
        let sum: i32 = data.iter().sum();
        println!("Sum of data: {}", sum);
        // data is dropped here at end of closure
    };
    compute();
    // println!("{:?}", data);  // ❌ Compile error: data was moved into the closure
    // compute();  // ❌ Could also fail if the closure consumed data

    println!("\n=== Practical: Closure Type Selection ===");
    let items = vec!["apple", "banana", "cherry"];

    // Fn: read-only
    let count = || items.len();
    println!("Count (Fn): {}", count());

    // FnMut: modify captured variable
    let mut store = String::new();
    let mut append = |item: &str| {
        if !store.is_empty() { store.push_str(", "); }
        store.push_str(item);
    };
    for item in &items {
        append(item);
    }
    println!("Store (FnMut modified): {}", store);
    let store_len = store.len();
    println!("Store length: {}", store_len);
}

输出:

TEXT 📖 仅展示
=== Fn: Immutable Borrow ===
Scores: [10, 20, 30]
Scores: [10, 20, 30]
Still accessible: [10, 20, 30]

=== FnMut: Mutable Borrow ===
Counters: [3, 3, 3]

=== FnOnce: Ownership Consumed ===
Consuming: Pizzaplace

=== FnOnce with move ===
Sum of data: 15

=== Practical: Closure Type Selection ===
Count (Fn): 3
Store (FnMut modified): apple, banana, cherry
Store length: 20

编译器根据闭包体对捕获变量的操作方式自动推断使用哪个 trait:只读访问 ➔ Fn,可变访问 ➔ FnMut(必须用 mut 声明闭包),消费所有权 ➔ FnOnce(只能调用一次)。move 关键字强制将所有权移入闭包——常用于多线程场景(thread::spawn 需要 'static 生命周期)。


▶ 示例 3:闭包作为参数——函数指针 vs Fn trait(难度 ⭐⭐)

RUST
// ============================================
// Closures as parameters: fn pointer vs Fn trait
// ============================================

// --- Version 1: Accepts ONLY function pointers ---
// fn(T) -> U is a function pointer type (no environment capture)
fn apply_fn_pointer(f: fn(i32) -> i32, x: i32) -> i32 {
    f(x)
}

// --- Version 2: Accepts ANY Fn trait (including closures) ---
// impl Fn(T) -> U accepts both fn pointers and closures
fn apply_fn<F>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    f(x)
}

// --- Version 3: FnMut parameter ---
fn apply_twice<F>(mut f: F, x: i32) -> i32
where
    F: FnMut(i32) -> i32,
{
    f(f(x))
}

// --- Version 4: FnOnce parameter ---
fn apply_once<F>(f: F, x: i32) -> i32
where
    F: FnOnce(i32) -> i32,
{
    f(x)
}

// A regular function (can be used as fn pointer)
fn square(x: i32) -> i32 {
    x * x
}

fn main() {
    // --- Regular functions as fn pointers ---
    println!("--- Function Pointers ---");
    // `square` is a function, can be passed as fn(i32) -> i32
    println!("square(5) via fn ptr: {}", apply_fn_pointer(square, 5));

    // Annotated closure (no capture) can be coerced to fn pointer
    let triple = |x: i32| x * 3;
    println!("triple(5) via fn ptr: {}", apply_fn_pointer(triple, 5));

    // --- Closures with Fn trait ---
    println!("\n--- Fn Trait (generic) ---");
    let base = 10;
    // This closure captures `base`, so it CANNOT be a fn pointer
    let add_base = |x: i32| x + base;
    println!("add_base(5): {}", apply_fn(add_base, 5));

    // Both fn pointers and closures work with Fn trait
    println!("square(6) via Fn: {}", apply_fn(square, 6));

    // --- FnMut in action ---
    println!("\n--- FnMut ---");
    let mut accum = 0;
    let mut accumulate = |x: i32| {
        accum += x;
        accum
    };
    println!("accumulate(5): {}", apply_twice(&mut accumulate, 5));
    // After apply_twice: accum = 5 + 5 = 10, then 10 + 5 = 15
    println!("Final accum: {}", accum);

    // --- FnOnce in action ---
    println!("\n--- FnOnce ---");
    let owned = String::from("value: ");
    let describe = |x: i32| {
        println!("{} {}", owned, x);
        x
    };
    println!("describe(42): {}", apply_once(describe, 42));
    // describe was consumed (FnOnce), cannot call again

    // --- Practical: scoring system with different strategies ---
    println!("\n--- Practical: Scoring Strategies ---");

    fn run_strategy<F>(reviews: &[u32], label: &str, strategy: F)
    where
        F: Fn(u32) -> u32,
    {
        let scores: Vec<u32> = reviews.iter().map(|&r| strategy(r)).collect();
        println!("{}: {:?}", label, scores);
    }

    let ratings = [4, 2, 5, 3, 1];
    let bonus_threshold = 4;
    let bonus_points = 1;

    // Different scoring strategies, all as closures
    run_strategy(&ratings, "Normal", |r| r);
    run_strategy(&ratings, "Double", |r| r * 2);
    run_strategy(&ratings, "Bonus", |r| if r >= bonus_threshold { r + bonus_points } else { r });
    run_strategy(&ratings, "Penalty", |r| if r < 3 { 0 } else { r });
}

输出:

TEXT 📖 仅展示
--- Function Pointers ---
square(5) via fn ptr: 25
triple(5) via fn ptr: 15

--- Fn Trait (generic) ---
add_base(5): 15
square(6) via Fn: 36

--- FnMut ---
accumulate(5): 15
Final accum: 15

--- FnOnce ---
value:  42
describe(42): 42

--- Practical: Scoring Strategies ---
Normal: [4, 2, 5, 3, 1]
Double: [8, 4, 10, 6, 2]
Bonus: [5, 2, 6, 3, 1]
Penalty: [4, 0, 5, 3, 0]

函数指针 fn(i32) -> i32 只能接受普通函数和不捕获环境的闭包。Fn trait(泛型)可以接受任何闭包(包括捕获环境的)。选择规则:接受 fn 指针用于 FFI 或需要存储固定的函数类型;接受 Fn/FnMut/FnOnce trait 用于需要闭包捕获环境的场景。run_strategy 函数接受 impl Fn(u32) -> u32——四种不同的评分策略作为闭包传入,包括捕获了 bonus_threshold 和 bonus_points 的 Bonus 策略。


▶ 示例 4:闭包与迭代器组合——map / filter / collect(难度 ⭐⭐⭐)

RUST
// ============================================
// Closures + Iterator combinators: map, filter, collect
// ============================================

#[derive(Debug)]
struct Order {
    store: String,
    items: Vec<String>,
    rating: u32,
}

fn main() {
    let orders = vec![
        Order {
            store: String::from("Pizzaplace"),
            items: vec!["Margherita".into(), "Cola".into()],
            rating: 4,
        },
        Order {
            store: String::from("BurgerKing"),
            items: vec!["Whopper".into(), "Fries".into(), "Shake".into()],
            rating: 2,
        },
        Order {
            store: String::from("SushiBar"),
            items: vec!["Salmon".into(), "Tuna".into()],
            rating: 5,
        },
        Order {
            store: String::from("NoodleHouse"),
            items: vec!["Ramen".into(), "Gyoza".into(), "Tea".into()],
            rating: 3,
        },
    ];

    // --- map: transform each element ---
    println!("--- map: store names uppercase ---");
    let store_names: Vec<String> = orders
        .iter()
        .map(|o| o.store.to_uppercase())
        .collect();
    println!("{:?}", store_names);

    // --- filter: keep elements matching a condition ---
    println!("\n--- filter: orders with rating >= 4 ---");
    let good_orders: Vec<&Order> = orders
        .iter()
        .filter(|o| o.rating >= 4)
        .collect();
    for o in &good_orders {
        println!("{:?}", o);
    }

    // --- filter + map: chained ---
    println!("\n--- filter + map: good store names ---");
    let good_stores: Vec<String> = orders
        .iter()
        .filter(|o| o.rating >= 4)
        .map(|o| format!("★ {} (rating: {})", o.store, o.rating))
        .collect();
    for s in &good_stores {
        println!("{}", s);
    }

    // --- map + filter: compute and refine ---
    println!("\n--- map + filter: average rating per item ---");
    let avg_ratings: Vec<String> = orders
        .iter()
        .map(|o| {
            // Compute average rating per item
            let item_count = o.items.len() as f64;
            let avg = o.rating as f64 / item_count;
            (o.store.clone(), avg, o.items.len())
        })
        .filter(|(_, avg, _)| *avg > 1.5)  // Only stores with high per-item rating
        .map(|(store, avg, count)| format!("{}: {:.2}/item ({} items)", store, avg, count))
        .collect();
    for s in &avg_ratings {
        println!("{}", s);
    }

    // --- Advanced: closures capturing external state ---
    println!("\n--- Advanced: dynamic threshold filtering ---");
    let min_rating = 3;
    let category = "Premium";

    let premium_stores: Vec<String> = orders
        .iter()
        .filter(|o| o.rating >= min_rating)
        .map(|o| format!("[{}] {} (rating: {})", category, o.store, o.rating))
        .collect();

    for s in &premium_stores {
        println!("{}", s);
    }

    // --- All items from high-rated stores ---
    println!("\n--- Flat items from high-rated stores ---");
    let threshold = 3;
    let recommended_items: Vec<&String> = orders
        .iter()
        .filter(|o| o.rating > threshold)
        .flat_map(|o| o.items.iter())
        .collect();
    println!("Recommended: {:?}", recommended_items);

    // --- Custom scoring with filter ---
    println!("\n--- Custom filter: weighted score ---");
    let weight = 1.2;
    let min_score = 4.0;

    let top_orders: Vec<&Order> = orders
        .iter()
        .filter(|o| (o.rating as f64 * weight) >= min_score)
        .collect();
    for o in &top_orders {
        let weighted = o.rating as f64 * weight;
        println!("{}: raw={}, weighted={:.1}, pass={}", o.store, o.rating, weighted, weighted >= min_score);
    }

    // --- count, any, all ---
    println!("\n--- Aggregate checks ---");
    let high_rated_count = orders.iter().filter(|o| o.rating >= 4).count();
    println!("Orders with rating >= 4: {}", high_rated_count);

    let has_bad_order = orders.iter().any(|o| o.rating <= 1);
    println!("Has any bad order (rating <= 1): {}", has_bad_order);

    let all_rated = orders.iter().all(|o| o.rating >= 1);
    println!("All orders rated at least 1: {}", all_rated);
}

输出:

TEXT 📖 仅展示
--- map: store names uppercase ---
["PIZZAPLACE", "BURGERKING", "SUSHIBAR", "NOODLEHOUSE"]

--- filter: orders with rating >= 4 ---
Order { store: "Pizzaplace", items: ["Margherita", "Cola"], rating: 4 }
Order { store: "SushiBar", items: ["Salmon", "Tuna"], rating: 5 }

--- filter + map: good store names ---
★ Pizzaplace (rating: 4)
★ SushiBar (rating: 5)

--- map + filter: average rating per item ---
Pizzaplace: 2.00/item (2 items)
SushiBar: 2.50/item (2 items)

--- Advanced: dynamic threshold filtering ---
[Premium] Pizzaplace (rating: 4)
[Premium] SushiBar (rating: 5)
[Premium] NoodleHouse (rating: 3)

--- Flat items from high-rated stores ---
Recommended: ["Margherita", "Cola", "Salmon", "Tuna"]

--- Custom filter: weighted score ---
Pizzaplace: raw=4, weighted=4.8, pass=true
SushiBar: raw=5, weighted=6.0, pass=true

--- Aggregate checks ---
Orders with rating >= 4: 2
Has any bad order (rating <= 1): false
All orders rated at least 1: true

迭代器组合子是闭包最强大的应用场景之一:map(转换每个元素)、filter(按条件保留元素)、flat_map(展平嵌套迭代器)、any/all(聚合检查)、count(计数)。闭包在这里的价值在于:可以捕获外部动态值(如 min_rating、weight、category),让数据处理的逻辑与配置解耦。链式调用 iter().filter().map().collect() 是 Rust 中最具表现力的惯用法。


▶ 示例 5:综合练习——闭包实现配置驱动的数据管道(难度 ⭐⭐⭐)

RUST
// ============================================
// 综合示例:闭包捕获 + 高阶函数 + 迭代器链
// ============================================

struct DataPipeline<'a, T> {
    data: Vec<T>,
    filters: Vec<Box<dyn Fn(&T) -> bool + 'a>>,
    transforms: Vec<Box<dyn Fn(T) -> T + 'a>>,
}

impl<'a, T: Clone + 'a> DataPipeline<'a, T> {
    fn new(data: Vec<T>) -> Self {
        DataPipeline { data, filters: Vec::new(), transforms: Vec::new() }
    }

    fn filter<F: Fn(&T) -> bool + 'a>(mut self, f: F) -> Self {
        self.filters.push(Box::new(f));
        self
    }

    fn map<F: Fn(T) -> T + 'a>(mut self, f: F) -> Self {
        self.transforms.push(Box::new(f));
        self
    }

    fn execute(self) -> Vec<T> {
        let mut result = self.data;
        for f in &self.filters {
            result.retain(f);
        }
        for t in &self.transforms {
            result = result.into_iter().map(t).collect();
        }
        result
    }
}

fn main() {
    let min_score = 60;
    let bonus = 10;
    let max_score = 100;

    let pipeline = DataPipeline::new(vec![85, 42, 95, 58, 73, 30, 88])
        .filter(move |&&x| x >= min_score)
        .filter(|&&x| x < max_score)
        .map(move |x| x + bonus);

    let result = pipeline.execute();
    println!("筛选+加分后: {:?}", result);

    let multiplier = 2;
    let adjusted: Vec<i32> = result.into_iter()
        .map(|x| x * multiplier)
        .filter(|x| *x < 200)
        .collect();
    println!("翻倍+上限过滤: {:?}", adjusted);

    let threshold = 150;
    let has_high = adjusted.iter().any(|&x| x > threshold);
    let all_above_100 = adjusted.iter().all(|&x| x > 100);
    println!("有超过{}的: {}, 全部>100: {}", threshold, has_high, all_above_100);
}

输出:

TEXT 📖 仅展示
筛选+加分后: [95, 83, 98]
翻倍+上限过滤: [190, 166, 196]
有超过150的: true, 全部>100: true

DataPipeline 用 Box<dyn Fn> 存储闭包,move 捕获外部配置值(min_score、bonus),链式调用 filter().map() 构建处理管道。闭包让"配置与逻辑分离"成为可能。


❓ 常见问题

Q 闭包和普通函数的根本区别是什么?
A 闭包可以捕获周围作用域的变量,普通函数不能。
Q Fn、FnMut、FnOnce 编译器是怎么选的?
A 编译器根据闭包体中对捕获变量的操作自动推断。
Q 什么时候需要用 move 关键字?
A 当你需要闭包拥有捕获变量的所有权时,特别是多线程场景。
Q 函数指针 fn(T) -> U 和闭包 trait Fn(T) -> U 可以互相替代吗?
A 不能完全替代。函数指针不能捕获环境,闭包可以。
Q 闭包作为参数时,impl Fn、Box<dyn Fn>、泛型 F: Fn 选哪个?
A 泛型约束 F: Fn 性能最好(静态分发),Box<dyn Fn> 最灵活(动态分发),impl Fn 是泛型的语法糖。
Q 闭包的性能如何?比普通函数慢吗?
A 不捕获环境的闭包和普通函数性能完全相同。捕获环境的闭包零开销或接近零开销。

📖 小节


📝 作业

  1. 难度 ⭐:有一个 Vec<i32> 包含 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]。写一个闭包捕获变量 threshold(设为 5),用 filter 筛选出大于 threshold 的数,用 map 将它们翻倍,最后 collect 到新的 Vec 中并打印。

  2. 难度 ⭐⭐:写一个函数 fn transform<F>(data: &[i32], f: F) -> Vec<i32> where F: Fn(i32) -> i32,对每个元素应用变换函数。在 main 中创建三个闭包:|x| x * 2(翻倍)、捕获变量 add 的加法闭包、捕获变量 max_val 的截断闭包(如果超过 max_val 就返回 max_val)。分别调用 transform 并打印结果。

  3. 难度 ⭐⭐⭐:有一个结构体 struct Product { name: String, price: f64, category: String }。创建 6 个产品实例放入 Vec。写一个函数 fn analyze_products<F1, F2>(products: &[Product], category_filter: F1, price_adjuster: F2) where F1: Fn(&&Product) -> bool, F2: Fn(f64) -> f64,先用 filter 筛选类别,再用 map 调整价格,最后按调整后的价格排序并打印前 3 个最贵的产品。在 main 中用不同的筛选和调价策略测试至少 2 次。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏