rust基础二(闭包)

大家好,我是鱼樱!!!

关注公众号【鱼樱AI实验室】持续分享更多前端和AI辅助前端编码新知识~~

不定时写点笔记写点生活~写点前端经验。

在当前环境下,纯前端开发者可以通过技术深化、横向扩展、切入新兴领域以及产品化思维找到突破口。

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

前端最卷的开发语言一点不为过,三天一小更,五天一大更。。。一年一个框架升级~=嗯,要的就是这样感觉!与时俱进~

概念

Rust 中的闭包(Closure)是一个可以捕获其环境的匿名函数。它类似于其他语言中的 lambda 表达式或匿名函数。

用法

  1. 基本语法

    rust 复制代码
    let closure = |参数| -> 返回类型 { 函数体 };
  2. 简单示例

    rust 复制代码
    let add_one = |x: i32| -> i32 { x + 1 };
    let result = add_one(5); // result = 6
    
    // 简化写法(类型推断)
    let add_two = |x| x + 2;
  3. 捕获环境变量

    rust 复制代码
    let x = 4;
    let equal_to_x = |y| y == x; // 闭包捕获了外部变量 x
    assert!(equal_to_x(4));
  4. 作为参数传递

  • 闭包的参数和返回值:闭包可以有零个或多个参数,并且可以返回一个值

  • 闭包的调用:闭包可以像函数一样被调用

  • 闭包在 Rust 中类似于匿名函数,可以在代码中以 {} 语法块的形式定义,使用 || 符号来表示参数列表

    rust 复制代码
    fn apply<F>(f: F, x: i32) -> i32 
    where 
        F: Fn(i32) -> i32 
    {
        f(x)
    }
    
    let double = |x| x * 2;
    let result = apply(double, 5); // result = 10
    
    // 闭包的参数和返回值:闭包可以有零个或多个参数,并且可以返回一个值
    let calculate = |a, b, c| a * b + c;
    // 闭包的调用:闭包可以像函数一样被调用
    let result = calculate(1, 2, 3);
  1. 在迭代器中使用

    rust 复制代码
    let numbers = vec![1, 2, 3, 4];
    let doubled: Vec<i32> = numbers.iter()
                                   .map(|x| x * 2)
                                   .collect();

闭包的三种特质

  1. Fn - 不可变借用捕获的变量
  2. FnMut - 可变借用捕获的变量
  3. FnOnce - 获取捕获变量的所有权
rust 复制代码
// Fn 示例 - 只读访问
let x = 5;
let print_x = || println!("{}", x);
print_x();
print_x(); // 可以多次调用

// FnMut 示例 - 可变访问
let mut y = 5;
let mut add_to_y = || {
    y += 1;
    println!("{}", y);
};
add_to_y(); // y = 6
add_to_y(); // y = 7

// FnOnce 示例 - 获取所有权
let z = String::from("hello");
let consume_z = move || {
    println!("{}", z); // z 的所有权被移动到闭包中
};
consume_z();
// consume_z(); // 错误!z 已经被移动,不能再次调用

注意事项

  1. 所有权转移

    • 使用 move 关键字可以强制闭包获取环境变量的所有权
    rust 复制代码
    let s = String::from("hello");
    let closure = move || println!("{}", s); // s 的所有权被移动到闭包中
  2. 生命周期

    • 闭包不能超过其捕获变量的生命周期
    rust 复制代码
    fn make_closure(x: &i32) -> impl Fn() -> i32 {
        move || *x // 需要 move 来确保闭包拥有 x 的所有权
    }
  3. 类型推断

    • 闭包的参数和返回类型可以自动推断,但普通函数不行
    rust 复制代码
    // 闭包 - 可以省略类型
    let square = |x| x * x;
    
    // 普通函数 - 必须指定类型
    fn square(x: i32) -> i32 { x * x }
  4. 性能考虑

    • 闭包通常会被内联,没有性能开销
    • 编译器会根据使用方式自动选择合适的闭包特质(Fn, FnMut, FnOnce
  5. 与函数指针的区别

    rust 复制代码
    // 函数指针
    fn add_one(x: i32) -> i32 { x + 1 }
    let f: fn(i32) -> i32 = add_one;
    
    // 闭包
    let add_one_closure = |x| x + 1;

闭包是 Rust 中非常强大的特性,广泛用于函数式编程风格、迭代器适配器和异步编程中。

Rust 闭包捕获外部变量的方式、特性、类型和生命周期

捕获方式

Rust 闭包可以通过三种方式捕获外部变量:

  1. 不可变借用(Immutable Borrow) - Fn trait
  2. 可变借用(Mutable Borrow) - FnMut trait
  3. 所有权转移(Move Ownership) - FnOnce trait

捕获特性和示例

1. 不可变借用(Fn)

rust 复制代码
let x = 5;
let equal_to_x = |y| y == x; // 不可变借用 x

println!("{}", equal_to_x(5)); // true
println!("{}", x); // 仍然可以使用 x,因为只是借用

2. 可变借用(FnMut)

rust 复制代码
let mut y = 5;
let mut add_to_y = || {
    y += 1; // 可变借用 y
    println!("y = {}", y);
};

add_to_y(); // y = 6
add_to_y(); // y = 7
println!("最终 y = {}", y); // 仍然可以使用 y

3. 所有权转移(FnOnce)

rust 复制代码
let s = String::from("hello");
let consume_s = || {
    println!("{}", s); // 获取 s 的所有权
    s // 返回 s,移动所有权
};

let result = consume_s();
// println!("{}", s); // 错误!s 的所有权已经被移动

使用 move 关键字强制所有权转移:

rust 复制代码
let x = vec![1, 2, 3];
let closure = move || {
    println!("{:?}", x); // x 的所有权被移动到闭包中
};
// x 在这里不再可用
closure();

闭包类型和特质

Rust 中的闭包实现了以下三个特质之一,编译器会根据闭包如何使用捕获的变量自动选择:

  1. Fn trait

    • 通过不可变引用访问捕获的变量
    • 可以多次调用
    rust 复制代码
    let x = 5;
    let print_x = || println!("{}", x);
    print_x();
    print_x(); // 可以多次调用
  2. FnMut trait

    • 通过可变引用访问捕获的变量
    • 可以多次调用,但可能改变状态
    rust 复制代码
    let mut count = 0;
    let mut increment = || {
        count += 1;
        println!("Count: {}", count);
    };
    increment(); // Count: 1
    increment(); // Count: 2
  3. FnOnce trait

    • 通过获取所有权访问捕获的变量
    • 只能调用一次(除非捕获的变量实现了 Copy)
    rust 复制代码
    let s = String::from("hello");
    let consume = || {
        println!("{}", s);
        s // 移动所有权
    };
    let result = consume();
    // consume(); // 错误!不能再调用

特质之间的关系:

rust 复制代码
// Fn 是最严格的,也自动实现了 FnMut 和 FnOnce
trait Fn<Args>: FnMut<Args> { ... }

// FnMut 自动实现了 FnOnce
trait FnMut<Args>: FnOnce<Args> { ... }

// FnOnce 是最基础的
trait FnOnce<Args> { ... }

生命周期

闭包的生命周期遵循以下规则:

1. 捕获引用的生命周期

rust 复制代码
fn make_closure(x: &i32) -> impl Fn() -> i32 + '_ {
    move || *x // 闭包必须不能超过 x 的生命周期
}

2. 返回闭包的生命周期

rust 复制代码
fn factory() -> impl Fn(i32) -> i32 {
    let num = 5;
    move |x| x + num // 使用 move 获取所有权
}

3. 闭包作为参数的生命周期

rust 复制代码
fn apply<F>(f: F) -> i32 
where
    F: Fn() -> i32,
{
    f()
}

let x = 10;
let closure = || x; // 捕获 x
let result = apply(closure);

实际应用示例

结合你提供的代码示例,我们可以扩展展示不同捕获方式:

rust 复制代码
fn main() {
    // 当前示例 - 没有捕获外部变量
    let calculate = |a, b, c| a * b + c;
    let result = calculate(1, 2, 3);
    println!("结果 = {}", result);
    
    // 捕获不可变引用
    let multiplier = 2;
    let multiply = |x| x * multiplier; // 捕获 multiplier
    println!("5 * 2 = {}", multiply(5));
    
    // 捕获可变引用
    let mut counter = 0;
    let mut increment = || {
        counter += 1;
        counter
    };
    println!("Counter: {}", increment()); // 1
    println!("Counter: {}", increment()); // 2
    
    // 使用 move 转移所有权
    let name = String::from("Rust");
    let greet = move || {
        println!("Hello, {}!", name); // 获取 name 的所有权
    };
    greet();
    // name 在这里不再可用
}

闭包的捕获机制使 Rust 能够在保证内存安全的同时提供灵活的函数式编程能力。编译器会自动选择最合适的捕获方式和特质实现。

闭包可以通过三种方式捕获外部变量

  • 按引用捕获 (默认行为,类似 &T
  • 按值捕获 (类似 T
  • 可变借用捕获 (类似 &mut T
rust 复制代码
fn main() {
    let mut num = 5;

    // 按引用捕获
    let print_num = || println!("num = {}", num);
    print_num(); // 输出: num = 5

    // 按值捕获
    let take_num = move || println!("num taken = {}", num);
    take_num(); // 输出: num taken = 5
    // println!("{}", num); // 若取消注释,将报错,num 所有权被转移

    // 可变借用捕获
    let mut change_num = || num += 1;
    change_num();
    println!("num after closure = {}", num); // 输出: num after closure = 6
}

总结

Rust 的闭包是一种强大的抽象,它们提供了一种灵活且表达力强的方式来编写函数。

闭包可以捕获环境变量,并且可以作为参数传递或作为返回值。闭包与迭代器结合使用,可以方便地实现复杂的数据处理任务。

Rust 的闭包设计考虑了安全性、性能和生命周期,是 Rust 语言的重要组成部分。

相关推荐
BillKu2 分钟前
Vue3数组去重方法总结
前端·javascript·vue.js
江城开朗的豌豆24 分钟前
Vue+JSX真香现场:告别模板语法,解锁新姿势!
前端·javascript·vue.js
这里有鱼汤31 分钟前
首个支持A股的AI多智能体金融系统,来了
前端·python
袁煦丞32 分钟前
5分钟搭建高颜值后台!SoybeanAdmin:cpolar内网穿透实验室第648个成功挑战
前端·程序员·远程工作
摸鱼仙人~33 分钟前
Vue.js 指令系统完全指南:深入理解 v- 指令
前端·javascript·vue.js
前端进阶者34 分钟前
支持TypeScript并打包为ESM/CommonJS/UMD三种格式的脚手架项目
前端
星空下的曙光34 分钟前
pnpm vs npm区别对比
前端·npm·node.js
啃火龙果的兔子35 分钟前
React 图标库发布到 npm 仓库
前端·react.js·npm
江城开朗的豌豆36 分钟前
Vue列表渲染的坑:为什么不能用index当key?血泪教训总结!
前端·javascript·vue.js
JiaLin_Denny37 分钟前
如何在在NPM发布一个React组件
前端·react.js·npm·npm组件·npm发布·npm发布组件·npm如何发布组件