当前位置: 首页 > news >正文

Rust中的闭包

Rust 的闭包在功能和用途上与其他语言的lambda相同,都是在本地定义的小型匿名函数,可以捕获外部变量。

基本语法

Rust 闭包的基本定义形式如下

  • |参数列表|:用竖线包裹参数,多个参数用逗号分隔(如|x, y|)。
  • 函数体:可以是单个表达式(省略{}),也可以是多个语句(需用{}包裹)。
let closure_name = |参数列表| -> 返回类型 {表达式或语句块
};

类型与特征

闭包在 Rust 中有独立的匿名类型,并自动实现以下特征之一:

Trait含义适用场景
Fn不可变捕获环境(只读访问)可多次调用,捕获的变量仍可在外部使用
FnMut可变捕获环境(可修改捕获的变量)可多次调用,但捕获的变量在外部需为mut
FnOnce转移捕获变量的所有权(move
关键字)
只能调用一次(所有权转移后无法再次使用)

示例

pub fn closure_test() {let greeting = String::from("Hi");// Fn(只读)let say_hi = || println!("{}", greeting);say_hi();println!("greeting: {}", greeting);// FnMut(可变)let mut num = 0;let mut add_one = || num += 1;add_one();add_one();println!("num: {}", num);// FnOnce(移动)let s = String::from("Rust");let consume = move || {let tmp = s; // 闭包内字符串被移动到tmp,失去所有权println!("{}", tmp);};consume();// consume();  // 编译错误:闭包内捕获的s已被移动// println!("s: {}", s); // 编译错误:s已被移动
}

move语义

默认情况下,闭包会根据对变量的操作自动选择捕获方式(不可变借用、可变借用),尽量避免所有权转移。而move关键字会强制闭包 “夺取” 捕获变量的所有权(对于非Copy类型),或复制变量(对于Copy类型)。

  • Copy类型(如String,Vec)的所有权具有唯一性,move会让闭包直接获取其所有权,原变量在闭包外不可再使用(所有权已转移)。
  • Copy类型在赋值时会自动复制,move会让闭包获取变量的副本,原变量仍可正常使用。

move与闭包Trait

move仅决定闭包如何捕获变量(所有权转移),但闭包最终实现Fn/FnMut/FnOnce中的哪个 trait,取决于闭包如何使用捕获的变量(而非move本身)。

move + 只读使用 → 实现Fn

move闭包仅只读访问捕获的变量(不修改、不消耗),则实现Fn trait,可多次调用。

let s = String::from("hello");
// move捕获s的所有权,闭包仅读取s
let closure = move || println!("{}", s);closure(); // 正常
closure(); // 正常(可多次调用,因未消耗s)
// println!("{}", s); // 编译错误:s的所有权已被转移到闭包中

move + 修改使用 → 实现FnMut

move闭包修改捕获的变量(但不消耗),则实现FnMut trait,闭包自身需为mut

////////////////////////////////////
// 非copy语义类型,所有权被转移
let mut v = vec![1, 2];
// move捕获v的所有权,闭包修改v
let mut closure = move || v.push(3);closure(); // v变为 [1,2,3]
closure(); // v变为 [1,2,3,3](可多次调用)
// println!("{}", v); // 编译错误:s的所有权已被转移到闭包中////////////////////////////////////
// copy语义类型,复制一个副本,修改不影响外部
let mut num = 0;
let mut add_one = move || {num += 1;println!("num: {}", num);
};
add_one(); // 1
add_one(); // 2
println!("num: {}", num); // 0

move + 消耗使用 → 实现FnOnce

move闭包消耗捕获的变量(如调用drop或转移所有权给其他对象),则实现FnOnce trait,只能调用一次。

fn test_fnonce() {let s = String::from("hello");// move捕获s的所有权,闭包消耗s(转移给println!)let closure = move || {println!("{}", s); s // 这里s被消耗};closure(); // 正常// closure(); // 编译错误:s已被消耗,闭包只能调用一次
}

典型使用场景

move主要用于解决生命周期问题:

  • 线程间传递闭包:线程的生命周期是独立的,无法保证外部变量的生命周期能覆盖线程的。此时move可将变量所有权转移到线程内部,避免悬垂引用
  • 延长变量生命周期:当闭包需要 “带走” 变量并在更晚的时候使用时,move可确保变量的所有权随闭包一起存在,避免提前销毁。

实现原理

闭包在编译时会被转换为一个匿名结构体类型(即使两个闭包代码完全相同,也会生成不同的类型)。结构体的字段为捕获的变量,实际字段类型取决于捕获方式(借用、所有权)。

  • 若不捕获任何变量:结构体为零大小(ZST,无字段),不占用内存空间,调用成本极低(编译器可完全优化掉)。
  • 若捕获多个变量:结构体字段按声明顺序连续存储(类似普通结构体)。
    • 捕获引用时:结构体中仅存储变量的引用;
    • 捕获所有权时:结构体存储被捕获变量的实际值(拥有所有权)。

闭包与对应的结构体:

let x = 10;
let y = String::from("hello");
let closure = |z| x + z; // 捕获x(不可变借用),不捕获y///////////////////////////////////////////////////
// 以上闭包会生成类似下面的
// 匿名结构体,命名为ClosureType仅作示例
struct ClosureType<'a> {x: &'a i32, // 捕获x的不可变引用(因仅读取x)
}impl<'a> Fn<(i32,)> for ClosureType<'a> {type Output = i32;// call方法的逻辑即闭包体:x + zfn call(&self, args: (i32,)) -> i32 {let (z,) = args; // 解构参数*self.x + z // 使用结构体字段x(不可变引用)}
}
http://www.dtcms.com/a/541086.html

相关文章:

  • 辽宁省建设信息网福州网站推广优化
  • Physical AI:赋能工业运营中的新一代生产力和生产关系
  • 网站关键词价格徐州网站建设新闻
  • Swift-Mapping: Online Neural Implicit Dense Mapping in Urban Scenes 论文学习记录
  • Rust性能优化与最佳实践:构建高性能服务端与客户端应用
  • 海口网站建设过程全国做网站的
  • 网站建设一般满足什么需求电商网站怎么制作
  • 【若依前后端分离版,docker部署到服务器流程详解】
  • 想注册一个设计网站吗自己做的网站403
  • 没有基础怎么学网站建设python做网站教程
  • 网站构建代码模板北京公司网站优化
  • Ruby 范围(Range)
  • C学习过程记录
  • 企业微信自建应用后通过api给用户发消息
  • 岳池发展建设集团有限公司门户网站怎样看网页的友情链接
  • 亚马逊网站怎么做软件开发网站建设维护
  • 【VLNs篇】11:Dynam3D: 动态分层3D令牌赋能视觉语言导航中的VLM
  • 算法复杂度
  • Quant4.0,基于AgentScope开发 | 年化316%,回撤14%的超级轮动策略,附python代码
  • 第三方检测机构如何选对LIMS?以“数治”破解效率与合规难题
  • 建设网站需要什么手续设计公司网站 唐山
  • 网站接入地查询织梦网站修改数据库表数据
  • 南昌企业做网站设计怎么设置微信公众号
  • esp32在vscode使用jtag下载调试openocd问题解决
  • Java奇幻漂流:从Spring秘境到微服务星辰的冒险指南
  • Ubuntu 20.04.6使用vscode从0开始搭建uniapp
  • 【PDF】PDF技术概述
  • 金仓KCSM认证攻略:经验分享
  • [Nginx] 3.由HTTP转发引出的重定向问题
  • 子网站如何做哪个网站有做兼职的