Rust编程(五)终章:查漏补缺

闭包 & 迭代器

闭包(Closure)通常是指词法闭包,是一个持有外部环境变量的函数。外部环境是指闭包定义时所在的词法作用域。外部环境变量,在函数式编程范式中也被称为自由变量,是指并不是在闭包内定义的变量。将自由变量和自身绑定的函数就是闭包。
闭包的两大特性:

  • 延迟执行。返回的闭包只有在需要调用的时候才会执行。
  • 捕获环境变量。闭包会获取其定义时所在作用域中的自由变量,以供之后调用时使用。

Rust的闭包语法形式参考了Ruby语言的lambda表达式:

fn main(){let add_op = |a:i32,b:i32| -> i32 {a+b};let c = add_op(1,2);println!("1+2={}",c);
}

闭包的语法都大差不差,rust,ruby,python等都差不多一个样,
在这里插入图片描述
rust闭包的具体实现方式可以看Rust编程之道里面有讲解,目前没用到闭包,这里就不做讲解了,看了不用也记不住。

智能指针

智能指针(Box, Rc, Arc, Cell, RefCell, Cow等等),对原始指针进行包装,并添加额外的语义。
Box 是最直接的智能指针——它将数据分配到堆上而非栈上,在栈上只留一个指向堆中数据的指针。Box 没有性能开销,在下面的情况下使用:

  • 递归类型,如链表和树,必须使用Box去包装引用自身的字段以保证编译时能确定大小
  • 数据太大,希望移动所有权时减少拷贝数据消耗
  • 希望持有特定 trait 的值,无关它的实际类型(即不知晓它的大小) 作者:_YKI https://www.bilibili.com/read/cv31741541/ 出处:bilibili

Rc:提供在堆中分配的 T 类型值的共享所有权。在 Rc 上调用克隆方法会生成一个指向堆中相同地址的新指针。当指向给定地址的最后一个 Rc 指针被销毁时,存储在该地址中的值也会被删除。

IO

Rust官方对于IO实现了IO trait,大部分函数看名字就知道作用

pub trait Read {// Required methodfn read(&mut self, buf: &mut [u8]) -> Result<usize>;// Provided methodsfn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { ... }//is_read_vectored目前还是nightly APIfn is_read_vectored(&self) -> bool { ... }fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { ... }fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { ... }fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { ... }fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { ... }fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { ... }fn by_ref(&mut self) -> &mut Selfwhere Self: Sized { ... }fn bytes(self) -> Bytes<Self> //是指将 Read 按逐字节的方式转换成迭代器。迭代器为Result<u8>,这个Result也是std::io::Result<T>where Self: Sized { ... }fn chain<R: Read>(self, next: R) -> Chain<Self, R> where Self: Sized { ... }fn take(self, limit: u64) -> Take<Self> where Self: Sized { ... }
}
pub trait Write {// Required methodsfn write(&mut self, buf: &[u8]) -> Result<usize>;fn flush(&mut self) -> Result<()>;// Provided methodsfn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { ... }fn is_write_vectored(&self) -> bool { ... }fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... }fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<()> { ... }fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()> { ... }fn by_ref(&mut self) -> &mut Selfwhere Self: Sized { ... }
}

很多类型实现了标准 IO 特型:File、TcpStream、Vec、&[u8],注意返回类型是std::io::Result,不是std::Result

pub type Result<T> = Result<T, Error>;

使用IO的demo:

use std::io::prelude::*;
use std::fs::File;fn main() -> std::io::Result<()> {let data = b"some bytes";let mut pos = 0;let mut buffer = File::create("foo.txt")?;while pos < data.len() {let bytes_written = buffer.write(&data[pos..])?;pos += bytes_written;}Ok(())
}

安全并发

Rust中的线程管理和线程同步工具相关库为:std::thread模块和std::sync模块。Rust中的线程是本地线程,每个线程都有自己的栈和本地状态。创建一个线程如下:

use std::thread;
fn main(){let mut v = vec![]for id in 0..5 {let child = thread::spawn(move || {println!("in child:{}",id);});//spawn是Rust中线程初始化的函数//直接使用thread::spawn生成的线程,默认没有名称,并且其栈大小默认为2MB。//这里使用move关键字来强行将捕获变量id的所有权转移到闭包中。v.push(child);}println!("in main join before:");for child in v {child.join(); //等待child结束后再接着运行//但是child之间并没有相互等待的关系,输出是乱序的}println!("int main joint after");
}

并发安全是Rust的一个卖点。Rust中内置了两个trait:std::marker::Send和std::marker::Sync,实现了Send的类型可以安全地在线程间传递所有权,即跨线程移动;实现了Sync的类型,可以安全地在线程间传递不可变借用,即跨线程共享。和Send/Sync相反的标记是!Send/!Sync,表示不能在线程间安全传递的类型。智能指针Rc实现了!Send/!Sync,因为Rc内部并不是原子操作,在线程间传递会导致技术不准确。Rust提供了线程安全版的Rc,即Arc,内部使用的是原子操作,可以在线程间安全传递。

这两个标记trait反映了Rust看待线程安全的哲学:多线程共享内存并非线程不安全问题所在,问题在于错误地共享数据。通过Send和Sync将类型贴上“标签”,由编译器来识别这些类型是否可以在多个线程之间移动或共享,从而做到在编译期就能发现线程不安全的问题。
《Rust编程之道》

至于为什么这两个trait就可以实现并发安全,这两个具体怎么用?Rust中的锁怎么写?这部分等到后面用到再仔细分析,目前没有写项目,学了也是纸上谈兵。

宏系统

在 Rust 中宏分为两大类:声明式宏( declarative macros ) macro_rules! 和三种过程宏( procedural macros ):

  • #[derive],在之前多次见到的派生宏,可以为目标结构体或枚举派生指定的代码,例如 Debug 特征
  • 类属性宏(Attribute-like macro),用于为目标添加自定义的属性
  • 类函数宏(Function-like macro),看上去就像是函数调用

声明宏就是与传统的编程语言中的宏的概念是差不多的,但过程宏是使用源代码作为输入参数,基于代码进行一系列操作后,再输出一段全新的代码。有点类似于Python中的装饰器一样。

macro_rules!用来声明一个声明宏,也可以叫生成规则宏,他可以定义代码生成规则,声明式宏允许我们写出类似 match 的代码。match 表达式是一个控制结构,其接收一个表达式,然后将表达式的结果与多个模式进行匹配,一旦匹配了某个模式,则该模式相关联的代码将被执行。语法格式

macro_rules! macro_name{() => { //跟match的语法非常像code;};
}

案例:

#[macro_export] //注释将宏进行了导出,这样其它的包就可以将该宏引入到当前作用域中,然后才能使用。
macro_rules! vec {($($x:expr),*) => ( { //这里的匹配规则有点像正则表达式一样,$x:expr表示可以匹配任何类型,*表示可以重复无数次let mut temp_vec = Vec::new();$(temp_vec.push($x);)*temp_vec} );
}

大部分人都是宏的使用者,很少编写,啥时候用到再学吧。

总结

Rust语言的卖点就是:安全,安全,还是安全。这个安全包括内存安全,并发安全。其实这两点Python都能做到,但是为什么还要推出Rust呢?性能是关键!
Python保证内存安全是使用GC垃圾回收机制,保证并发安全是使用GIL锁。这两个机制都会极大降低Python的运行速度【目前据说GIL锁在新版本的Python中已经可以移除了,但是Python的速度还是没法跟Rust比】。
Rust确保这两点安全的方法是:所有权、声明周期机制以及强大的类型系统。所有权系统和生命周期确保了一份数据在同一时刻只会被一个变量所拥有,有效避免了悬垂指针以及双重释放等内存问题。内存安全的Bug和并发安全的Bug产生的内在原因是相同的,都是因为内存的不正当访问而造成的。同样,利用装载了所有权的强大类型系统,Rust还解决了并发安全的问题。Rust编译器会通过静态检查分析,在编译期就检查出多线程并发代码中所有的数据竞争问题。

类型系统是指编程语言中对于类型的一套规则,并不是真的有个系统,类型系统的主要作用就是类型推导,类型检测,类型转换,Rust是类型安全的,即:在运行时不会出现类型错误。

Rust作为后起之秀,借鉴了很多语言的设计思想,Haskell,C/C++,Python,Ruby等等,像智能指针,模板特化等很多新的技术都有借鉴,这些也一定程度上确保了内存安全。

谈完Rust设计哲学,再谈一谈Rust的编程范式,不用说,基本上所有现代语言都是混合编程范式的,理论上Rust可以支持各种编程范式,但是哪种支持更完备,编程更方便,自然是不一样的。在Rust中,不管哪一种单一编程范式来进行编程都很难受,面向对象,函数式,泛型编程,都是这样的。对于面向对象,Rust中有struct、方法和trait来实现面向对象。对于函数式,Rust中有闭包以及一切皆类型的类型系统。对于泛型编程,Rust有泛型,有模板特化,const模板参数等支持。所以对于Rust项目,比较推荐的编程范式是:轻度面向对象+泛型编程,函数式编程做为边边角角的辅助使用。更多可以看一下这篇文章

至此Rust编程就已经完结了,泛型编程和宏系统的应用,Rust设计模式的实现,异步编程和数据并行编程,unsafe特性,与其他语言相互调用,等等等等,这些特性我建议用到再学,因为学完不用很快就忘了,没必要,有一些概念不清楚的,不要去百度,去看Rust圣经或者直接去看Rust标准库官方文档,写的非常明确。
下一步就是开始从0写操作系统,清华的操作系统训练营还有好几天才开营,先学起来,下一篇打算跟着Rust实战这本书写一个CPU模拟器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2906005.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

小狐狸JSON-RPC:钱包连接,断开连接,监听地址改变

detect-metamask 创建连接&#xff0c;并监听钱包切换 一、连接钱包&#xff0c;切换地址&#xff08;监听地址切换&#xff09;&#xff0c;断开连接 使用npm安装 metamask/detect-provider在您的项目目录中&#xff1a; npm i metamask/detect-providerimport detectEthereu…

主从复制与读写分离

前言&#xff1a; 在企业应用中&#xff0c;成熟的业务通常数据量都比较大&#xff0c;单台MySQL在安全性、高可用性和高并发方面 都无法满足实际的需求&#xff1f; 配置多台主从数据库服务器以实现读写分离 一 主从复制的工作原理 ①Master节点将数据的改变记录成二进制…

机器学习算法的另一个分支-贝叶斯算法原理(贝叶斯要解决什么问题)

目录 一、贝叶斯简介 二、贝叶斯要解决的问题 三、例子&#xff08;公式推导&#xff09; 四、实例 1. 拼写纠正实例 2. 垃圾邮件过滤实例 一、贝叶斯简介 1. 贝叶斯&#xff1a;英国数学家。1702年出生于伦敦&#xff0c;做过神甫。贝叶斯在数学方面主要研究概率论.对于…

使用unplugin-auto-import页面不引入api飘红

解决方案&#xff1a;. tsconfig.json文件夹加上 {"compilerOptions": {"target": "ES2020","useDefineForClassFields": true,"module": "ESNext","lib": ["ES2020", "DOM", &q…

【2024系统架构设计】案例分析- 4 嵌入式

目录 一 基础知识 二 真题 一 基础知识 1 基本概念 ◆系统可靠性是系统在规定的时间内及规定的环境条件下,完成规定功能的能力,也就是系统无故障运行的概率。或者,可靠性是软件系统在应用或系统错误面前,在意外或错误使用的情况下维持软件系统的功能特性的基本能力。

Discuz采集发布插件

Discuz&#xff08;简称DZ&#xff09;是一款知名的开源论坛系统&#xff0c;广泛应用于各类网站社区。对于许多站长来说&#xff0c;保持论坛内容的更新是一项挑战&#xff0c;特别是在内容量庞大的情况下。为了解决这个问题&#xff0c;有一类特殊的插件是用于在Discuz论坛中…

论文阅读-Policy Optimization for Continuous Reinforcement Learning

摘要 我们研究了连续时间和空间环境下的强化学习( RL )&#xff0c;其目标是一个具有折扣的无限时域&#xff0c;其动力学由一个随机微分方程驱动。基于连续RL方法的最新进展&#xff0c;我们提出了占用时间(专门针对一个折现目标)的概念&#xff0c;并展示了如何有效地利用它…

使用mysql官网软件包安装mysql

确定你的操作系统&#xff0c;我的是Centos myqsl 所有安装包的地址&#xff1a;https://repo.mysql.com/yum/ 如果你是使用rpm安装你可以到对应的版本里面找到对应的包。 mysql 发行包的地址&#xff1a;http://repo.mysql.com/ 在这里你可以找到对应的发布包安装。 这里使用y…

Windows下安装使用Squirrel

引言 SQuirrel SQL Client是一个用Java写的数据库客户端,用JDBC统一数据库访问接口以后,可以通过一个统一的用户界面来操作MySQL PostgreSQL MSSQL Oracle等等任何支持JDBC访问的数据库。使用起来非常方便。而且,SQuirrel SQL Client还是一个典型的Swing程序。 如果您的工作…

git:git rm --cached和git rm -f和git restore --staged的区别(附带详细步骤测试)和git diff比较本地分支和远程分支的区别(细分到文件/文件)

git rm --cached和git rm -f和git restore --staged的区别 当试图删除一个已经git add在暂存区的文件&#xff0c;我们使用 git rm --cached&#xff1a;从暂存区中移除&#xff0c;但保留在工作区中&#xff0c;且工作区中的文件内容在执行命令前需要还原到最后一次git add的…

统信 UOS V20 一键安装 Oracle 11GR2(231017)单机版

Oracle 一键安装脚本&#xff0c;演示 统信 UOS V20 一键安装 Oracle 11GR2&#xff08;231017&#xff09;单机版过程&#xff08;全程无需人工干预&#xff09;&#xff1a;&#xff08;脚本包括 ORALCE PSU/OJVM 等补丁自动安装&#xff09; ⭐️ 脚本下载地址&#xff1a;…

100 个 Kotlin 面试问题及答案(其二)

尤其是在Android开发中&#xff0c;Kotlin已经成为一种流行的编程语言。为了帮助您在 Kotlin 面试中取得成功&#xff0c;我们为您简化了 100 个最常见的面试问题。本指南涵盖了广泛的主题&#xff0c;包括基本语言概念和高级功能。每个问题都附有简单的答案和实际示例&#xf…

Webpack常见插件和模式

目录 目录 目录认识 PluginCleanWebpackPluginHtmlWebpackPlugin自定义模版 DefinePlugin的介绍 ( 持续更新 )Mode 配置 认识 Plugin Loader是用于特定的模块类型进行转换&#xff1b; Plugin可以用于执行更加广泛的任务&#xff0c;比如打包优化、资源管理、环境变量注入等 …

Voodoo中国区刘毅:全球爆款休闲游戏的创意选品与研发发行 | TopOn观察

10月28日&#xff0c;由TopOn联合罗斯基主办的“游戏赛道新机会”主题沙龙在成都举办。活动邀请了多位国内外知名公司及游戏爆款产品的负责人分享&#xff0c;分别从各自的方向及经验出发&#xff0c;以数据、案例、产品分析、行业趋势等多个维度&#xff0c;为行业从业者带来独…

DataX-Oracle新增writeMode支持update

目录 前言 第一步下载源码 第二步修改源码 1、Oraclewriter 2、WriterUtil 2.1、修改getWriteTemplate方法 2.2、新增onMergeIntoDoString与getStrings方法 3、CommonRdbmsWriter 3.1、修改startWriteWithConnection 3.2、修改doBatchInsert 3.3、修改fillPreparedStatem…

QT6实现音频输出方法

一.QT6音频调用及与QT5的区别 1.音频输入 QAudioSource代替QAudioInput类 QAudioSource类提供了一个接口&#xff0c;用于从音频输入设备接收音频数据。 Header: #include <QAudioSource> qmake: QT multimedia 2.音频输出 QAudioSink代替QAudioOutput类 QAudioSi…

硬件10、从网站获取封装

百度搜索IC封装网或者网址https://www.iclib.com/ 搜索想要的器件&#xff0c;直接下载他的原理图库和封装库

Spring Cloud+Spring Alibaba笔记

Spring CloudSpring Alibaba 文章目录 Spring CloudSpring AlibabaNacos服务发现配置中心 OpenFeign超时机制开启httpclient5重试机制开启日志 SeataSentinel流量控制熔断降级热点控制规则持久化集成 OpenFeign集成 Gateway MicrometerZipKinGateway路由断言过滤器 Nacos 服务…

【YOLOv5改进系列(6)】高效涨点----使用DAMO-YOLO中的Efficient RepGFPN模块替换yolov5中的Neck部分

文章目录 &#x1f680;&#x1f680;&#x1f680;前言一、1️⃣ 添加yolov5_GFPN.yaml文件二、2️⃣添加extra_modules.py代码三、3️⃣yolo.py文件添加内容3.1 &#x1f393; 添加CSPStage模块 四、4️⃣实验结果4.1 &#x1f393; 使用yolov5s.pt训练的结果对比4.2 ✨ 使用…

【Pytorch入门】小土堆PyTorch入门教程完整学习笔记(详细笔记并附练习代码 ipynb文件)

小土堆PyTorch入门教程笔记 最近在观看PyTorch深度学习快速入门教程&#xff08;绝对通俗易懂&#xff01;&#xff09;【小土堆】顺便做点笔记&#xff0c;方便回看&#xff0c;同时也希望记录的笔记能够帮助到更多在入门的小伙伴~ 【注】仅记录个人觉得重要的知识&#xff0c…