2024 Rust现代实用教程 closures闭包

文章目录

一、闭包基础概念

闭包是一种可以捕获其环境中变量的匿名函数

闭包的语法相对简洁灵活,同时也具有强大的功能。闭包在 Rust 中被广泛用于函数式编程、并发编程以及简化代码等方面。

1.如何使用闭包

定义闭包的语法类似 (但更简单)

  • 在II内定义参数
  • 可选地指定参数/返回类型
  • 在{}内定义闭包体

你可以将闭包分配给一个变量,然后使用该变量,就像它是一个函数名,来调用闭包

Example:

rust 复制代码
#[derive(Debug)]
struct User {
    name: String,
    score: u64,
}

// 老派写法
// sort_by_key
// fn sort_score(users: &mut Vec<User>) {
//     users.sort_by_key(sort_helper);
// }

// fn sort_helper(u: &User) -> u64 {
//     u.score
// }

// 新式写法:直接使用闭包
fn sort_score_closure(users: &mut Vec<User>) {
    users.sort_by_key(|u| u.score);
}

fn main() {
    let f = |a, b| a + b;
    println!("{}", f(1.0, 2.0));

    let a = User {
        name: "U1".to_owned(),
        score: 100,
    };
    let b = User {
        name: "U2".to_owned(),
        score: 80,
    };
    let c = User {
        name: "U3".to_owned(),
        score: 40,
    };
    let d = User {
        name: "U4".to_owned(),
        score: 90,
    };
    let mut users = vec![a, b, c, d];
    // sort_score(&mut users);
    sort_score_closure(&mut users);
    println!("{:?}", users);
}

编译及运行:

cpp 复制代码
 cargo run
   Compiling ch29_closure v0.1.0 (/home/wangji/installer/rust/project/ch29_closure)
warning: field `name` is never read
 --> src/main.rs:3:5
  |
2 | struct User {
  |        ---- field in this struct
3 |     name: String,
  |     ^^^^
  |
  = note: `User` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
  = note: `#[warn(dead_code)]` on by default

warning: `ch29_closure` (bin "ch29_closure") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.22s
     Running `target/debug/ch29_closure`
3
[User { name: "U3", score: 40 }, User { name: "U2", score: 80 }, User { name: "U4", score: 90 }, User { name: "U1", score: 100 }]

二、闭包获取参数byreference与byvalue

1.获取外部参数

由Rust编译器决定用那种方式获取外部参数

1.不可变引用 Fn

2.可变引用FnMut

3.转移所有权(Move)FnOnce

2.所有权转移move

Rust编译器判断capturesby value

  • 比方说在闭包手动drop该参数

move关键字强制将所有权转移到闭包

Example:

rust 复制代码
fn main() {
    // Fn不可变引用获取外部参数
    let s1 = String::from("1111111111111111111");
    let s2 = String::from("2222222222222222222");
    let fn_func = |s| {
        println!("{s1}");
        println!("I am {s}");
        println!("{s1}");
    };
    fn_func("yz".to_owned());
    fn_func("原子".to_owned());
    println!("{s1} {s2}");

    // FnMut 可变引用获取外部参数
    let mut s1 = String::from("1111111111111111111");
    let mut s2 = String::from("2222222222222222222");
    let mut fn_func = |s| {
        s1.push_str("😀");
        s2.push_str("😀");
        println!("{s1}");
        println!("I am {s}");
        println!("{s1}");
    };
    fn_func("yz".to_owned());
    fn_func("原子".to_owned());
    println!("{s1} {s2}");

    // 所有权转移 由编译器根据我们的代码来判读
    let s1 = String::from("1111");
    let fn_Once_func = || {
        println!("{s1}");
        std::mem::drop(s1); //销毁了
    };
    fn_Once_func();
    // println!("{s1}");
    // 捕获的参数强制move
    let s1 = String::from("1111");
    let move_fn = move || {
        println!("{s1}");
    }; // Fn : FnMut : FnOnce
    move_fn();
    // println!("{s1}");
    let s1 = String::from("1111");
    std::thread::spawn(move || println!("d  {s1}")); //move确保线程运行的时候,s1还在
}

编译及运行:

bash 复制代码
 cargo run
   Compiling ch29_func v0.1.0 (/home/wangji/installer/rust/project/ch29_func)
warning: variable `fn_Once_func` should have a snake case name
  --> src/main.rs:30:9
   |
30 |     let fn_Once_func = || {
   |         ^^^^^^^^^^^^ help: convert the identifier to snake case (notice the capitalization): `fn_once_func`
   |
   = note: `#[warn(non_snake_case)]` on by default

warning: `ch29_func` (bin "ch29_func") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.35s
     Running `target/debug/ch29_func`
1111111111111111111
I am yz
1111111111111111111
1111111111111111111
I am 原子
1111111111111111111
1111111111111111111 2222222222222222222
1111111111111111111😀
I am yz
1111111111111111111😀
1111111111111111111😀😀
I am 原子
1111111111111111111😀😀
1111111111111111111😀😀 2222222222222222222😀😀
1111
1111

三、闭包是怎么工作的

1.闭包在底层是怎么工作的?

1.Rust编译器将闭包放入一个结构体

2.结构体会声明一个cal丨function,而闭包就是函数,cal丨function会包含闭包的所有代码

3.结构体会生产一些属性去捕获闭包外的参数

4.结构体会实现一些特质

  • FnOnce
  • FnMut
  • Fn

2.FnOnce,FnMut,Fn特质

Fn继承至FnMut,FnMut继承至FnOnce

Example:

rust 复制代码
fn apply_closure<F: Fn(i32, i32) -> i32>(closure: F, x: i32, y: i32) -> i32 {
    closure(x, y)
}

fn main() {
    let x = 5;
    let add_closure = |a, b| {
        println!("x is: {}", x);
        a + b + x
    };
    let result = apply_closure(add_closure, 5, 6);
    println!("{}", result);
}

编译及运行:

bash 复制代码
 cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/ch30_closure_trait`
x is: 5
16

Example:可变应用的例子

  • 能写非mut的尽量写非mut的,因为mut和非mut的性能差距很大
rust 复制代码
fn apply_closure<F: FnMut(i32, i32) -> i32>(mut closure: F, x: i32, y: i32) -> i32 {
    closure(x, y)
}

fn main() {
    let mut x = 5;
    let mut add_closure = |a, b| {
        x += 1;
        println!("x is: {}", x);
        a + b + x
    };
    let result = apply_closure(add_closure, 5, 6);
    println!("{}", result);
}

编译及运行

bash 复制代码
 cargo run
   Compiling ch30_closure_trait v0.1.0 (/home/wangji/installer/rust/project/ch30_closure_trait)
warning: variable does not need to be mutable
 --> src/main.rs:7:9
  |
7 |     let mut add_closure = |a, b| {
  |         ----^^^^^^^^^^^
  |         |
  |         help: remove this `mut`
  |
  = note: `#[warn(unused_mut)]` on by default

warning: `ch30_closure_trait` (bin "ch30_closure_trait") generated 1 warning (run `cargo fix --bin "ch30_closure_trait"` to apply 1 suggestion)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.33s
     Running `target/debug/ch30_closure_trait`
x is: 6
17

四、闭包类型FnOnce、FnMut和Fn做函数参数的实例

Example:

rust 复制代码
// 只有不可变引用能用
fn closure_fn<F>(func: F)
where
    F: Fn(),
{
    func();
    func();
}

// 可变引用和不可变引用都可以接受
fn closure_fn_mut<F>(mut func: F)
where
    F: FnMut(),
{
    func();
    func();
}

// 可变引用和不可变引用都可以接受,具有修改所有权的能力!!
fn closure_fn_once<F>(func: F)
where
    F: FnOnce(),
{
    func();
}

fn main() {
    // 不可变引用只能传一种
    let s1 = String::from("11111");
    closure_fn(|| println!("{}", s1)); //性能第2
                                       // 可变引用
    let s1 = String::from("11111");
    closure_fn_mut(|| println!("{}", s1)); //性能最差
                                           // println!("{}", s1);

    let mut s2 = String::from("22222");
    closure_fn_mut(|| {
        s2.push_str("😀");
        println!("{}", s2);
    });
    println!("{s2}");
    println!("======================");
    // 所有权转移
    let s1 = String::from("11111");
    closure_fn_once(|| println!("{}", s1));

    let mut s2 = String::from("22222");
    closure_fn_once(|| {
        s2.push_str("😀");
        println!("{}", s2);
    });
    println!("{s2}");

    let s3 = " ff".to_owned();
    closure_fn_once(move || println!("{s3}")); //move主动修改所有权,性能最好
                                               // println!("{s3}")
}

编译及运行

bash 复制代码
 cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/ch31_fn`
11111
11111
11111
11111
22222😀
22222😀😀
22222😀😀
======================
11111
22222😀
22222😀
 ff

参考

相关推荐
SomeB1oody17 小时前
【Rust自学】4.1. 所有权:栈内存 vs. 堆内存
开发语言·后端·rust
SomeB1oody1 天前
【Rust自学】4.2. 所有权规则、内存与分配
开发语言·后端·rust
SomeB1oody1 天前
【Rust自学】4.5. 切片(Slice)
开发语言·后端·rust
编码浪子2 天前
构建一个rust生产应用读书笔记6-拒绝无效订阅者02
开发语言·后端·rust
baiyu332 天前
1小时放弃Rust(1): Hello-World
rust
baiyu332 天前
1小时放弃Rust(2): 两数之和
rust
Source.Liu2 天前
数据特性库 前言
rust·cad·num-traits
编码浪子2 天前
构建一个rust生产应用读书笔记7-确认邮件1
数据库·rust·php
SomeB1oody2 天前
【Rust自学】3.6. 控制流:循环
开发语言·后端·rust
Andrew_Ryan2 天前
深入了解 Rust 核心开发团队:这些人如何塑造了世界上最安全的编程语言
rust