searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Rust闭包

2025-11-28 09:36:01
0
0

Rust 闭包学习笔记

一、闭包捕获环境变量的三种方式

Rust 的闭包可以从其定义环境中捕获变量,捕获方式取决于闭包内部对变量的使用方式。主要有以下三种:

  • 以不可变借用 &T 捕获
  • 以可变借用 &mut T 捕获
  • 以所有权 T 捕获

Rust 会根据闭包内部的操作自动推断需要的捕获方式。

1 不可变捕获:

let color = String::from("blue");

let print = || {
    println!("Color is {}", color);
};

这里闭包只读取 color,所以捕获方式为不可变借用 &T。
调用闭包之后,依然可以继续以不可变方式借用 color。

2 可变捕获:

let mut count = 0;

let mut increment = || {
    count += 1;
    println!("count is now {}", count);
};

因为闭包改变了 count,所以捕获方式变为 &mut。
同时,闭包本身需要用 mut 绑定,因为调用闭包会改变其内部状态。

3 所有权捕获:

let movable = Box::new(1);

let consume = || {
    println!("movable is now {}", movable);
    mem::drop(movable);
};

这里闭包对 movable 调用了 drop,因此闭包以 T 的方式捕获了变量,并且闭包只能调用一次。

二、使用 move 强制所有权捕获

在闭包定义前使用 move 关键字,可以强制闭包获得捕获变量的所有权。

let v = vec![1,2,3];

let my_contains = move |element| -> bool {
    v.contains(element)
};

此时 v 的所有权已移交给闭包,外部不可再访问 v。

三、闭包作为函数参数:Fn / FnMut / FnOnce

Rust 使用三个 trait 来描述闭包的捕获方式:

  • Fn:闭包捕获变量的方式只能是不可变借用(&T)
  • FnMut:闭包可能捕获可变借用(&mut T)
  • FnOnce:闭包可能捕获所有权(T)

其中 FnOnce 的等级最高,Fn的等级最低。

// 闭包作为函数参数时,需要以 泛型+trait 来实现
fn apply<T>(f: T) where 
    T: FnOnce() { 
    f(); 
}

let greeting = "hello";
let mut farewell = "goodbye".to_owned();

let diary = || {
    // 不可变借用,属于 Fn
    println!("I said {}", greeting);   
    // 可变借用,使闭包升级为 FnMut
    farewell.push_str("hahahaha");
    // 所有权消耗,使闭包升级为 FnOnce 
    mem::drop(farewell);                
};
apply(diary);

let do_nothing = || {println!(""); };
// apply的参数等级是FnOnce,当然能接受一个低等级的Fn(反过来就不行了!)
apply(do_nothing);

四、闭包作为函数返回值

闭包可以作为函数的输入,自然也可以作为函数的输出。函数可以返回一个闭包,但闭包必须使用 move 捕获外部变量,以避免悬垂引用。

fn create_fn() -> impl Fn(u32) -> u32 {
    let ret = move |x: u32| {
        x + 1
    };
    ret
}

let my_fn = create_fn();
println!("{}", my_fn(1));  // 输出 2

五、闭包在迭代器中的实际应用

迭代器大量依赖闭包,例如 any、find 等。

1 any

println!("2 in vec1: {}", vec1.iter().any(|&x| x == 2));
println!("2 in vec2: {}", vec2.into_iter().any(|x| x == 2));
  • iter() 产生的是 &i32,所以闭包需要 &x 来模式匹配
  • into_iter() 产生的是 i32 值,因此闭包直接 |x| 即可

2 find

println!("Find 2 in vec1: {:?}", iter.find(|&&x| x == 2));
println!("Find 2 in vec2: {:?}", into_iter.find(|&x| x == 2));

iter() 返回 &i32,find 提供的是 &&i32,因此需要 |&&x|。

0条评论
作者已关闭评论
陈****然
13文章数
0粉丝数
陈****然
13 文章 | 0 粉丝
陈****然
13文章数
0粉丝数
陈****然
13 文章 | 0 粉丝
原创

Rust闭包

2025-11-28 09:36:01
0
0

Rust 闭包学习笔记

一、闭包捕获环境变量的三种方式

Rust 的闭包可以从其定义环境中捕获变量,捕获方式取决于闭包内部对变量的使用方式。主要有以下三种:

  • 以不可变借用 &T 捕获
  • 以可变借用 &mut T 捕获
  • 以所有权 T 捕获

Rust 会根据闭包内部的操作自动推断需要的捕获方式。

1 不可变捕获:

let color = String::from("blue");

let print = || {
    println!("Color is {}", color);
};

这里闭包只读取 color,所以捕获方式为不可变借用 &T。
调用闭包之后,依然可以继续以不可变方式借用 color。

2 可变捕获:

let mut count = 0;

let mut increment = || {
    count += 1;
    println!("count is now {}", count);
};

因为闭包改变了 count,所以捕获方式变为 &mut。
同时,闭包本身需要用 mut 绑定,因为调用闭包会改变其内部状态。

3 所有权捕获:

let movable = Box::new(1);

let consume = || {
    println!("movable is now {}", movable);
    mem::drop(movable);
};

这里闭包对 movable 调用了 drop,因此闭包以 T 的方式捕获了变量,并且闭包只能调用一次。

二、使用 move 强制所有权捕获

在闭包定义前使用 move 关键字,可以强制闭包获得捕获变量的所有权。

let v = vec![1,2,3];

let my_contains = move |element| -> bool {
    v.contains(element)
};

此时 v 的所有权已移交给闭包,外部不可再访问 v。

三、闭包作为函数参数:Fn / FnMut / FnOnce

Rust 使用三个 trait 来描述闭包的捕获方式:

  • Fn:闭包捕获变量的方式只能是不可变借用(&T)
  • FnMut:闭包可能捕获可变借用(&mut T)
  • FnOnce:闭包可能捕获所有权(T)

其中 FnOnce 的等级最高,Fn的等级最低。

// 闭包作为函数参数时,需要以 泛型+trait 来实现
fn apply<T>(f: T) where 
    T: FnOnce() { 
    f(); 
}

let greeting = "hello";
let mut farewell = "goodbye".to_owned();

let diary = || {
    // 不可变借用,属于 Fn
    println!("I said {}", greeting);   
    // 可变借用,使闭包升级为 FnMut
    farewell.push_str("hahahaha");
    // 所有权消耗,使闭包升级为 FnOnce 
    mem::drop(farewell);                
};
apply(diary);

let do_nothing = || {println!(""); };
// apply的参数等级是FnOnce,当然能接受一个低等级的Fn(反过来就不行了!)
apply(do_nothing);

四、闭包作为函数返回值

闭包可以作为函数的输入,自然也可以作为函数的输出。函数可以返回一个闭包,但闭包必须使用 move 捕获外部变量,以避免悬垂引用。

fn create_fn() -> impl Fn(u32) -> u32 {
    let ret = move |x: u32| {
        x + 1
    };
    ret
}

let my_fn = create_fn();
println!("{}", my_fn(1));  // 输出 2

五、闭包在迭代器中的实际应用

迭代器大量依赖闭包,例如 any、find 等。

1 any

println!("2 in vec1: {}", vec1.iter().any(|&x| x == 2));
println!("2 in vec2: {}", vec2.into_iter().any(|x| x == 2));
  • iter() 产生的是 &i32,所以闭包需要 &x 来模式匹配
  • into_iter() 产生的是 i32 值,因此闭包直接 |x| 即可

2 find

println!("Find 2 in vec1: {:?}", iter.find(|&&x| x == 2));
println!("Find 2 in vec2: {:?}", into_iter.find(|&x| x == 2));

iter() 返回 &i32,find 提供的是 &&i32,因此需要 |&&x|。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0