Rust 学习笔记(卷二)

文章目录

  • Rust 学习笔记(卷二)
    • 八、工程
      • 1. package 和 crate
        • package 总览
        • 包根(crate root)
      • 2. 模块
        • 初识模块
        • 单个源文件中的嵌套模块
        • 具有层级结构的源文件形成的嵌套模块
        • 小结 use 语句
      • 3. 文档
      • 4. 使用第三方包
      • 5. 打包自己的包
    • 九、标准库
    • 十、多线程的并发编程
    • 十一、“不安全”编程
        • unsafe 代码块
        • 全局变量与静态变量
        • 内部可变性

Rust 学习笔记(卷二)

八、工程

相比以前的 C++,Rust 提出了包和模块的概念,使工程管理变得更加有组织。下面我们会自顶向下的介绍 Rust 中有关工程的概念。

1. package 和 crate

package 总览

Rust 工程管理中,最大的概念是 package,其次是 crate。Rust 的 crate 可以是一个库,也可以是一个可执行文件,而 package 的作用则是将一个或多个 crate 组织起来。

Package 是使用 Cargo.toml 文件管理的,这是我们所熟悉的,而 crate 则有组织地被放在项目文件夹中,包括:

  1. 库包。需要存在 src/lib.rs 源文件。包名与项目名相同。一个项目只能有一个库包。
  2. 可执行文件包。需要存在 src/main.rs 源文件。包名与项目名相同。
  3. 更多可执行文件包。每个 src/bin/ 中的源文件都是一个包,包名与文件名相同。

对比 C++ 来看,package 可以看作 CMake 的 project,而 crate 可以看作 CMake 的 target。

包根(crate root)

前面提到,如果一个 package 包含唯一的库包0,则需要存在 src/lib.rs 文件;对于可执行文件包则需要存在 src/main.rs。像这种一个源文件代表一个 crate 的,我们称为包根(crate root)。

包根只是一个源文件,那多个源文件该怎么办?方法是借助模块。模块的机制允许我们将包内的代码放在多个源文件中。

2. 模块

初识模块

Rust 中,与模块相关的关键字是 mod

Rust 程序 79:mod
// src/orange.rs
pub fn hello() {println!("Hello, orange!");
}
// src/main.rs
mod orange;
use orange::hello;
fn main() {hello();
}
C++ 程序 79:module
// orange.cpp
export module orange; // 手动写明,与文件名本身无关。
import std;
namespace orange { // 手动创建命名空间,与模块无关。export void hello() {std::cout << "Hello, orange!" << std::endl;}
}
// 源.cpp
import orange;
using orange::hello;
int main() {hello();
}

关于 Rust 程序 79 的新东西有:

  1. pub 关键字。如果要使用的东西不属于自己的父模块(例如程序 79 中,orange 模块和 main 模块是兄弟,orange 模块不是 main 模块的父模块),则只能使用公开的模块,用 pub 关键字表示。这和 C++ 程序 79 中 hello 函数前的 export 关键字一样。
  2. mod 关键字。其功能是在包根所在目录查找指定名字的源文件,并将源文件中的内容视为位于同名模块中(例如程序 79 中,在包根 main.rs 同目录下找到了 orange.rs,将 orange.rs 中的内容视为位于模块 orange 中)。
  3. use 关键字。一旦模块进入视野内(例如程序 79 中通过 mod 语句在当前位置声明了一个模块,又如通过配置依赖项已经引入了外部模块),则可以使用 use 语句引入模块里的内容。使用 use 语句引入内容的可见性将在之后讲解。

与 C++ 要求必须在单独的文件里定义模块不同,Rust 中允许在同一个源文件里定义模块。所以 Rust 的模块更像是 C++ 的模块和命名空间的结合体。

Rust 程序 80:只有包根
mod orange {pub fn hello() {println!("Hello, orange!");}
}
use orange::hello;
fn main() {hello();
}
C++ 程序 80:假装是模块
import std;
namespace orange { // 只有命名空间,没有模块。void hello() {std::cout << "Hello, orange!" << std::endl;}
}
using orange::hello;
int main() {hello();
}

Rust 程序 80 中,mod 块前没有再加 pub,这是因为我们在下面使用它时已经能够看到它的完整实现了,所以没有必要再加上 pub

Rust 中,为什么不像 C++ 那样必须写 import std;?因为 Rust 已经帮我们把常用的模块导入,模块名为 std::prelude

单个源文件中的嵌套模块

如前所述,Rust 的模块就像 C++ 中模块和命名空间的组合。在单个源文件中,模块就可以嵌套定义,形成树形结构。

Rust 程序 81:模块树
// 模块路径为 crate::orange。使用 crate 表示根模块,对应包根。
mod orange {pub struct Demo {pub name: String, // 模块外只能访问结构体的 pub 字段。}// 模块路径为 crate::orange::details。mod details {// 使用 super 表示模块树中的父模块。可省略。impl super::Demo {pub fn print(&self) {println!("{}", self.name);}}}
}// main 的模块路径为 crate::main。
fn main() {// 使用 self 表示当前模块。只能使用 pub 的内容。use self::orange::Demo;let demo = Demo {name: String::from("Orange"),};demo.print(); // 只需让实现的方法为 pub,无需让 impl 的模块为 pub。
}
C++ 程序 81:命名空间树
import std;
// 命名空间路径为 ::orange。使用 :: 表示全局命名空间。
namespace orange {struct Demo {public: // 类外只能访问结构体的 public 成员。std::string name;};// 命名空间路径为 ::orange::details。namespace details {// 父命名空间可省略。void print(const Demo& self) {std::cout << std::format("{}", self.name) << std::endl;}}
}// main 的命名空间路径为 ::main。
int main() {using orange::Demo;const auto demo = Demo{.name = std::string("Orange"),};// C++ 不支持在多个命名空间内实现类的扩展方法。using namespace orange::details;print(demo);
}

具有层级结构的源文件形成的嵌套模块

如前所述,创建除包根外的源文件会导致新建一个模块。例如,包根 src/main.rs 对应的模块是 crate,而与包根位于同一目录的 src/another.rs 对应的模块则是 crate::another。这是我们在程序 79 中已经学会的。但如果我们希望用一个单独的源文件表示模块 crate::another::yet_another,该怎么做?

方法是创建文件夹。容易想到,可以在 src/ 新建一个名为 another 的文件夹,再在 another 文件夹中新建 yet_another.rs 源文件。

src/
|--main.rs
|--another/|--yet_another.rs

但这是不够的。Rust 规定,对于 another 这种用文件夹表示的层级子模块,需要在文件夹中新建名为 mod.rs 的源文件,并在其中显式的导入其中的子模块。所以合规的文件结构应该为:

src/
|--main.rs
|--another/|--mod.rs          # 每个子文件夹中都必须有 mod.rs。|--yet_another.rs

亦或者,在文件夹外新建一个与文件夹同名的源文件。

src/
|--main.rs
|--another.rs        # 或者把 mod.rs 换成与模块同名的源文件放在外面。
|--another/|--yet_another.rs

以上两种方式只能取其一。下面以 mod.rs 为例。mod.rs 的内容应该为:

pub mod yet_another;

main.rs 的开头还应该有:

mod another; // 包根 main.rs 不需要导出模块,所以不加 pub。

在完成以上准备工作后,要在 main.rs 中使用 yet_another 中的内容,可以写:

use another::yet_another::*; // ::* 表示导入所有内容。

为什么需要写以上 mod 语句和 use 语句?下面我们总结一下 Rust 从包根开始搜索模块的过程。

  1. Rust 编译器只知道存在包根(main.rslib.rs)。包根对应的模块为根模块 crate
  2. 如果包根源文件不使用 mod 语句声明模块,则编译器不知道存在其他模块。
  3. 使用 mod 语句引入模块 yet_another,编译器发现存在文件夹 another,于是编译 src/another/mod.rssrc/another.rs,如果同时存在则报错。
  4. 编译 src/another/mod.rs 时,检查到 pub mod yet_another,编译器发现不存在文件夹 src/another/yet_another,但存在 src/another/yet_another.rs,于是将该源文件作为模块 crate::another::yet_another 编译。
  5. main.rs 中,已经能看到 crate::another。由于 yet_anotherpub 的,所以也能看到模块 crate::another::yet_another。使用 use 语句简化其中内容的使用。

一个很现实的问题是,子模块有时希望导出自己导入的内容(例如 another 模块希望导出 another::yet_another 模块),有时又希望不导出导入的内容,仅仅在内部自己使用(例如 crate 模块不会导出 crate::another 模块)。显然我们可以用 pub 关键字控制这一点。不过,上面的例子的导出对象都是模块,如果我们仅仅想要导出一个已经实现的函数该怎么办?可以使用 pub use 语句将当前模块导入的内容再导出。外部使用时,看这个导出的内容就好像是该模块自己实现的一样。

Rust 程序 82:引入项再导出
// orange.rs
mod utils {pub fn print() {println!("Orange");}// 可见,但限制只能在父模块中可见。pub(super) fn internal_print() {println!("Orange is handsome.");}
}// pub use 语句只能再导出 pub 的内容。
pub use utils::print; // 通过 use 语句实现了导出。pub fn orange_main() {print(); // use 语句的效果。utils::internal_print();
}// main.rs
mod orange;
fn main() {orange::orange_main();orange::print(); // pub use 语句的效果。
}
C++ 程序 82:引入项再导出
// orange.ixx
export module orange;
import std;namespace orange {namespace utils {void print() {std::cout << "Orange" << std::endl;}void internal_print() {std::cout << "Orange is handsome." << std::endl;}}// export using 语句可以直接导出非 export 的内容。export using utils::print;export void orange_main() {print(); // using 语句的效果。utils::internal_print();}
}// main.cpp
import orange;
int main() {orange::orange_main();orange::print(); // export using 语句的效果。
}

小结 use 语句

最后,我们对 use 语句作一些补充,并作一个简单的小结。可以说 Rust 中的 use 语句几乎等价于:

  1. C++ 中的 using 语句(后接函数等)和 using namespace 语句(后接命名空间)的结合。
  2. C# 中的 using static 语句(后接函数等)和 using 语句(后接命名空间)的结合。
  3. Java 中的 import 语句(后接函数或包)。
  4. ……

但 Rust 与 C++ 更类似,在使用 use 语句前,必须能够看到希望使用的内容。Rust 中使用 mod 语句从包根开始声明自己希望使用的模块,而 C++ 使用 import 语句声明自己希望使用的模块。相比之下,C#、Java 等语言不需要用额外语句导入模块。

很显然,use 语句能够帮助我们省略冗长的包路径,是一个缩写助手。所以 use 语句也能为所使用的内容起别名。

Rust 程序 83:使用别名
fn main() {use std::print as my_print;my_print!("Hello, world!");use std as my_std;my_std::path::Path::new("src/main.rs");
}
C++ 程序 83:使用别名
import std;
int main() {// C++ 不允许用 using 语句为函数或对象起别名。auto& my_cout = std::cout;my_cout << "Hello, world!" << std::endl;// C++ 中使用 namespace 语句为命名空间起别名。namespace my_std = std;my_std::filesystem::path("源.cpp");
}

3. 文档

4. 使用第三方包

5. 打包自己的包

九、标准库

十、多线程的并发编程

并发、异步。

十一、“不安全”编程

unsafe 代码块

全局变量与静态变量

理论上,全局变量越少越好,以防形成一盘散沙之势。然而,全局变量通常也是无法避免的,例如在使用单例设计模式时。

内部可变性

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

融媒行业落地客户旅程编排,详解数字化用户运营实战

移动互联网时代是流量红利的时代&#xff0c;企业常用低成本的方式进行获客&#xff0c;“增长黑客”的概念大范围传播。与此同时&#xff0c;机构媒体受到传播环境的影响&#xff0c;也开始启动全行业的媒体融合转型。在此背景下&#xff0c;2015 年神策数据成立&#xff0c;核…

港联证券:为什么好股票拿不住?股票怎么买更赚钱?

股票是一种高危险高收益的出资方式&#xff0c;要想挣钱仍是需求掌握一些技巧。那么为什么好股票拿不住&#xff1f;股票怎样买更挣钱&#xff1f;港联证券也为大家准备了相关内容&#xff0c;以供参考。 为什么好股票拿不住&#xff1f; 1、心态不稳。许多出资者缺少长时间的…

Java版工程行业管理系统源码-专业的工程管理软件-提供一站式服务 em

​ 鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工…

bootloader串口更新程序[瑕疵学习板]

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、储备知识二、程序步骤2.程序展示1.bootloader2.然后是主运行函数总结前言 很久没有更新文章了。最近工作太忙,没有学习很多的知识,然后这两天不忙了,就学习了一下bootloader的程序升级…

【已解决】pycharm突然双击无法打开,重启电脑也不管用

1.问题&#xff1a; pycharm突然双击无法打开&#xff0c;重启电脑也不管用 2.解决 2.1 方法一&#xff08;修改Roaming&#xff09; 1.找到C盘对应路径下的pycharm版本 2. 用记事本打开文件类型为VMOPTIONS文件 3. 修改或删除最后一行的映射路径 4.保存退出 2.2 方法二…

2023.8.28日论文阅读

文章目录 NestFuse: An Infrared and Visible Image Fusion Architecture based on Nest Connection and Spatial/Channel Attention Models(2020的论文)本文方法 LRRNet: A Novel Representation Learning Guided Fusion Network for Infrared and Visible Images本文方法学习…

phpspreadsheet导出excel自动获得列,数字下标

安装composer require phpoffice/phpspreadsheetuse PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Border;$spreadsheet new Spreadsheet(); $sheet $spreadsheet->getActiveSheet();//从65开&a…

Android 13 Ethernet变更

Android13 有线变更 以太网相关的功能在Android12 和13 网络部分变化是不大的&#xff0c;Android11 到Android 12 网络部分无论是代码存放目录和代码逻辑都是有较多修改的&#xff0c;主要包括以下几个部分 限制了设置有线网参数设置接口方法 新增有线网开启关闭接口方法 新…

实现人物关系图还在用Echarts吗?快试试relation-graph

关于relation-graph 支持Vue和React的 关联关系图谱组件&#xff0c;可以展示如组织机构图谱、股权架构图谱、集团关系图谱等知识图谱&#xff0c;可提供多种图谱布局&#xff0c;包括树状布局、中心布局、力学布局自动布局等。官网 安装使用 一&#xff0c;通过nodejs开发v…

java主要的垃圾回收算法

垃圾收集算法了解吗&#xff1f; 标记-清除算法 标记 : 标记出所有需要回收的对象 清除&#xff1a;回收所有被标记的对象 主要存在两个缺点&#xff1a; 执行效率不稳定&#xff0c;如果 Java 堆中包含大量对象&#xff0c;而且其中大部分是需要被回收的&#xff0c;这时必…

经济大环境不好是你给自己找的理由吗?

最近很多自媒体博主都在说的一个现象&#xff0c;就是今年的经济形势比口罩那几年都要难过&#xff0c;全球的经济都面临打的挑战&#xff0c;就业岗位的缺失&#xff0c;22-35岁的青年失业率攀升很多人都在痛苦的边缘挣扎。 我国灵活就业人数已超2亿&#xff0c;平台经济快速发…

水论文的三种套路

目录 1、换模型不换领域&#xff08;同领域换基准模型&#xff09;2、换领域不换模型&#xff08;同基准模型换领域&#xff09;3、改进的模型 1、换模型不换领域&#xff08;同领域换基准模型&#xff09; 有一个&#xff0c;对Transformer做了一个改进A&#xff0c;做视频描…

C语言这么没用??

今日话题&#xff0c;C语言真的这么不堪吗&#xff1f;最近我兄弟向我倾诉&#xff0c;他在几天前受到老板的责骂&#xff0c;原因是他只懂C语言编程&#xff0c;无法达到老板的期望。其实不是C语言不堪&#xff0c;而是嵌入式领域复杂性多种多样&#xff0c;需要灵活的解决方案…

如何使用ADX指标呢?10秒教会你

这是使用ADX大佬的收益结果&#xff0c;这是没有使用ADX技术指标的新手表情&#xff0c;事实证明只要会使用ADX指标&#xff0c;交易的结果就是令人可喜的&#xff0c;那么如何使用ADX指标呢?anzo capital昂首资本10秒教会你。 从评估价格方向、模式和水平开始技术分析。使用…

未来科技城携手加速科技 共建集成电路测试公共服务平台!

8月26日&#xff0c;2023未来产业发展大会在杭州未来科技城国际会议中心开幕&#xff01;会上&#xff0c;发布了未来科技城培育发展未来产业行动计划&#xff0c;启动了未来产业发展共同体&#xff0c;进行了未来产业公共服务平台签约仪式。未来科技城与加速科技签约共建集成电…

【PHP面试题81】php-fpm是什么?它和PHP有什么关系

文章目录 &#x1f680;一、前言&#xff0c;php-fpm是什么&#x1f680;二、php-fpm与PHP之间的关系&#x1f680;三、php-fpm解决的问题&#x1f50e;3.1 进程管理&#x1f50e;3.2 进程池管理&#x1f50e;3.3 性能优化&#x1f50e;3.4 并发处理 &#x1f680;四、php-fpm常…

论文笔记: 循环神经网络进行速度模型反演 (未完)

摘要: 分享对论文的理解, 原文见 Gabriel Fabien-Ouellet and Rahul Sarkar, Seismic velocity estimation: A deep recurrent neural-network approach. Geophysics (2020) U21–U29. 作者应该是领域专家, 对地球科学的理解胜于深度学习. 为方便讨论, 等式编号保持与原文一致.…

软件设计师知识点·1

控制器: (1)指令寄存器(IR) : CPU执行一条指令时,从内存储器取到缓冲寄存器中,再送入IR暂存; (2)程序计数器(PC): 将要执行的下一条指令的地址; (3)地址寄存器(IR): 当前CPU所访问的内存单元地址; (4)指令译码器(ID): 对指令中的操作码字段进行分析解释; 多核CPU可以满足用户…

Web安全测试(四):XML注入和代码注入

一、前言 结合内部资料&#xff0c;与安全渗透部门同事合力整理的安全测试相关资料教程&#xff0c;全方位涵盖电商、支付、金融、网络、数据库等领域的安全测试&#xff0c;覆盖Web、APP、中间件、内外网、Linux、Windows多个平台。学完后一定能成为安全大佬&#xff01; 全部…

最简单的爬虫!只需要粘贴你要爬虫的网址

依赖 必须按照这个库&#xff0c;爬虫必备&#xff01; pip install requests四行代码 import get import save spider_text get.html("https://www.baidu.com/") save.file(spider_text)使用 更改 get.html() 中的参数&#xff0c;改成你想要爬取的网站的地址&…