Rust学习记录--C13 Part1 闭包和迭代器

C13 Part1 闭包和迭代器

  • [13.1 闭包(closures)](#13.1 闭包(closures))
    • [13.1.1 函数式编程](#13.1.1 函数式编程)
    • [13.1.2 闭包的定义](#13.1.2 闭包的定义)
  • [13.2 闭包:类型推断和标注](#13.2 闭包:类型推断和标注)
    • [13.2.1 闭包的类型推断](#13.2.1 闭包的类型推断)
    • [13.2.2 函数和闭包的定义语法](#13.2.2 函数和闭包的定义语法)
    • 注意
  • [13.3 闭包:使用泛型和Fn Trait存储闭包](#13.3 闭包:使用泛型和Fn Trait存储闭包)
    • [13.3.1 记忆化(延迟计算)模式](#13.3.1 记忆化(延迟计算)模式)
    • [13.3.2 如何让struct持有闭包](#13.3.2 如何让struct持有闭包)
    • [13.3.3 Fn Trait](#13.3.3 Fn Trait)
  • [13.4 闭包:使用闭包捕获上下文](#13.4 闭包:使用闭包捕获上下文)
    • [13.4.1 闭包可以捕获他们所在的环境](#13.4.1 闭包可以捕获他们所在的环境)
    • [13.4.2 闭包从所在环境捕获值的方式](#13.4.2 闭包从所在环境捕获值的方式)
    • [13.4.3 move关键字](#13.4.3 move关键字)

13.1 闭包(closures)

13.1.1 函数式编程

  • 把函数作为参数
  • 将函数作为其他参数的返回值
  • 把函数赋给一个变量,被以后执行

13.1.2 闭包的定义

  • 闭包:可以捕获其所在环境的匿名函数
  • 特点
    • 是匿名函数
    • 保存为变量,作为参数
    • 可以在一个地方创建闭包,然后在另一个上下文中调用闭包来完成运算
    • 可从其定义的作用域捕获值
  • 例子
    • 仅在必要时调用该算法
    • 只调用一次
      已知下面的代码:
      thread_cost_long_time会被调用多次,并且thread_cost_long_time会比较耗时,需要使用闭包来改进下面的代码
rust 复制代码
fn thread_cost_long_time(time: u32) -> u32 {
    println!("Cost long time ....");
    thread::sleep(Duration::from_secs(2));
    time
}

fn generate_workout(time: u32, random_number: u32)
{
    if time < 25 
    {
        println!("cost time {} seconds", thread_cost_long_time(time));
        println!("Again, cost time {} seconds", thread_cost_long_time(time));
    } else {
        if random_number == 3 {
            println!("Break down")
        } else {
            println!("Again, and again cost time {} seconds", thread_cost_long_time(time));
        }
         
    }
}

改进1:

rust 复制代码
fn thread_cost_long_time(time: u32) -> u32 {
    println!("Cost long time ....");
    thread::sleep(Duration::from_secs(2));
    time
}

fn generate_workout(time: u32, random_number: u32)
{
		// 改成在if调用之前,这样就不会调用两次
		// 但是 会导致每次都需要计算,(下面的代码逻辑是当random_number =3的时候是不需要long_cost 的值的)=>会导致浪费
    let long_cost = thread_cost_long_time(time);
    if time < 25 
    {
        println!("cost time {} seconds", long_cost);
        println!("Again, cost time {} seconds", long_cost);
    } else {
        if random_number == 3 {
            println!("Break down")
        } else {
            println!("Again, and again cost time {} seconds", long_cost);
        }
         
    }
}

期望:函数定义在一个地方,真正需要结果时才执行相关的代码

  • 闭包定义
    定义一个匿名函数
    // 保存为一个变量long_closure
    ||之间是函数参数,如果有两个参数的话使用,分隔
    {}内部是闭包的函数体
    注意它是一个赋值语句,最后需要添加;
rust 复制代码
let long_closure = |time| {
    println!("Cost long time ....");
    thread::sleep(Duration::from_secs(2));
    time
};
  • 闭包的调用
    long_closure(time)
rust 复制代码
fn generate_workout(time: u32, random_number: u32)
{
    let long_closure = |time| {
        println!("Cost long time ....");
        thread::sleep(Duration::from_secs(2));
        time
    };

    if time < 25 
    {
        println!("cost time {} seconds", long_closure(time));
        println!("Again, cost time {} seconds", long_closure(time));
    } else {
        if random_number == 3 {
            println!("Break down")
        } else {
            println!("Again, and again cost time {} seconds", long_closure(time));
        }
         
    }
}

13.2 闭包:类型推断和标注

13.2.1 闭包的类型推断

  • 闭包不要求标注参数和返回值的类型
  • 闭包通常很短小,编译器通常能推出类型
  • 可以手动添加类型标注
rust 复制代码
    let long_closure = |time:u32| -> u32 {
        println!("Cost long time ....");
        thread::sleep(Duration::from_secs(2));
        time
    };

13.2.2 函数和闭包的定义语法

函数

fn add_one_v1 (x: u32) -> u32 { x+1 }

闭包

let add_one_v2 = | x: u32| -> u32 { x+1 };

let add_one_v3 = | x | { x+1 };

let add_one_v4 = | x | x+1 ;

注意

闭包的定义最终只会为参数/返回值推断出唯一具体的类型

例子:

rust 复制代码
    let example_closure = |x|x;

    let s = example_closure(String::from("Hello"));
    // 在这里会报错,它会要求example_closure传入的参数类型是String
    let n = example_closure(10);

13.3 闭包:使用泛型和Fn Trait存储闭包

13.3.1 记忆化(延迟计算)模式

  • 记忆化(延迟计算)模式:创建一个struct,它持有闭包及其调用结果
    • 只会在需要结果时才执行该闭包
    • 可缓存结果->结果不同时可以使用map

13.3.2 如何让struct持有闭包

  • struct的定义需要知道所有字段的类型
    • 需要指明闭包的类型
  • 每个闭包实例都有自己的唯一的匿名类型,即使两个闭包签名完全一样
  • 需要使用泛型和Trait Bound

13.3.3 Fn Trait

  • Fn Trait由标准库提供

  • 所有的闭包都至少实现了以下trait之一:

    • Fn
    • FnMut
    • FnOnce
  • 例子

rust 复制代码
fn generate_workout(time: u32, random_number: u32)
{
		// Cacher::new 里面传入的是一个闭包,即caculation代表一个闭包
    let mut long_closure = Cacher::new(|time| {
        println!("Cost long time ....");
        thread::sleep(Duration::from_secs(2));
        time
    });

    if time < 25 
    {
        println!("cost time {} seconds", long_closure.value(time));
        println!("Again, cost time {} seconds", long_closure.value(time));
    } else {
        if random_number == 3 {
            println!("Break down")
        } else {
            println!("Again, and again cost time {} seconds", long_closure.value(time));
        }
         
    }
}

struct Cacher<T>
where T : Fn(u32)->u32,
{
    caculation: T,
    value: Option<u32>
}

impl<T> Cacher<T>
where T : Fn(u32)->u32, {
    fn new(caculation: T) -> Cacher<T> {
        Cacher { caculation, value: None }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
        		// 如果value是有值的,直接返回
            Some(v) => v,
            None => {
            		// 如果value是没有值的,则执行一下caculation并且将arg赋值给对应的value
            		// 注意:(self.caculation)(arg)是使用caculation闭包执行函数,我第一次没有看懂
                let v = (self.caculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}

限制:

  1. Cacher实例假定针对不同的参数arg,value方法总会得到相同的值
    -> 使用HashMap代替单个值:
    key: arg参数
    value:执行闭包的结果
    修改为使用HashMap存储Value
rust 复制代码
struct Cacher<T>
where T : Fn(u32)->u32,
{
    caculation: T,
    value: HashMap<u32, u32>
}

impl<T> Cacher<T>
where T : Fn(u32)->u32, {
    fn new(caculation: T) -> Cacher<T> {
        Cacher { caculation, value: HashMap::new() }
    }

    fn value(&mut self, arg: u32) -> u32 {

        *self.value.entry(arg).or_insert((self.caculation)(arg))

    }
}

#[test]
fn test_return_some_res()
{
    let mut cacher = Cacher::new(|x| x+1);
    let x1 = cacher.value(1);
    assert_eq!(x1, 2);

    let x2 = cacher.value(2);
    assert_eq!(x2, 3);
}
  1. 只能接收一个u32类型的参数和u32类型的返回值
    -> 引入多个泛型

13.4 闭包:使用闭包捕获上下文

13.4.1 闭包可以捕获他们所在的环境

  • 闭包可以访问定义它的作用域内的变量,而普通函数则不行
  • 会产生内存开销
  • 例子
rust 复制代码
    let x = 4;
    let equal_to_x = |z| z==x;

    let y = 3;
    println!("{}", equal_to_x(y));
    println!("{}", x);

输出结果:

false

4

13.4.2 闭包从所在环境捕获值的方式

  • 与函数获得参数的三种方式一样:
  1. 取得所有权:FnOnce
  2. 可变借用:FnMut
  3. 不可变借用:Fn
  • 创建闭包时,通过闭包对环境值得使用,Rust推断出具体使用哪个trait:
    • 所有得闭包都实现了FnOnce
    • 没有移动捕获变量得实现了FnMut
    • 无需可变访问捕获变量得闭包实现了Fn

13.4.3 move关键字

  • 在参数列表前使用move关键字
    • 强制闭包取得它所使用得环境值得所有权
    • 当闭包传递给新线程以移动数据使其归新线程所有时,此技术最为有用
      例子
rust 复制代码
let x = vec![1,2,3];
let equal_to_x = move |z| x.contains(z);

let y = 3;
println!("{}", equal_to_x(&y));
println!("{:?}", x);

2026/1/16

相关推荐
洲星河ZXH2 小时前
Java,泛型
java·开发语言·windows
木木木一2 小时前
Rust学习记录--C13 Part2 闭包和迭代器
开发语言·学习·rust
Wcy30765190662 小时前
文件包含漏洞及PHP伪协议
开发语言·php
CopyProfessor2 小时前
Java Agent 入门项目模板(含代码 + 配置 + 说明)
java·开发语言
枫叶丹42 小时前
【Qt开发】Qt系统(八)-> Qt UDP Socket
c语言·开发语言·c++·qt·udp
代码游侠2 小时前
ARM 嵌入式开发学习——从内核到外设
arm开发·笔记·嵌入式硬件·学习
码农客栈2 小时前
小程序学习(十二)之命令行创建uni-app项目
学习·小程序·uni-app
爱问问题的小李2 小时前
学习面试题
学习
xiaobobo33302 小时前
STM32学习HAL库的一些知识点积累
stm32·学习·硬件软件一盘棋·寄存器操作·寄存器功能搜索