Rust如何进行模块化开发?

news2024/11/17 7:30:48

类似es6的模块化,Rust通过package、create、module来实现代码的模块化管理

Rust如何进行模块化开发?

Rust的代码组织包括:哪些细节可以暴露,哪些细节是私有的,作用域内哪些名称有效等等。

而这些功能被统称为模块系统,模块系统被分为(由上到下层层包含):

  • Package(包):Cargo的特性,让你构建、测试、共享create
  • Create(单元包):一个模块树,它可以产生一个library或可执行文件
  • Module(模块)、use:让你控制代码的组织、作用域、私有路径
  • Path(路径):为struct、function或module等项命名的方式

Package和Create

create的类型:

  • binary(二进制create)
  • library(库create)

其中,关于Create,还有个概念——Create Root:

  • 是源代码文件
  • Rust编译器从这里开始,组成你的Create的根Module

一个Package:

  • 包含一个Cargo.toml,它描述了如何构建这些Crates
  • 只能包含0-1个library create(库create)
  • 可以包含任意数量的binary create(二进制create)
  • 但必须至少包含一个create(library或binary)

我们使用cargo新建一个项目

然后会提示: Created binary (application) my-project package,这代表我们创建了一个二进制的应用程序,名叫my-project的package

我们进入这个文件夹:

我们可以看到src/min.rs文件,这是我们程序的入口文件,但是我们在Cargo.toml中并没有看到相关的配置:

[package]
name = "my-project"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies] 

这是因为cargo有一些惯例

Cargo的惯例

  • src/main.rs是binary create的create root* create的名与package名相同如果我们还有一个这个文件:src/lib.rs,那么:

  • 表明package包含一个library create

  • 它是library create的create root

  • create的名与package名相同

Cargo将会把create root文件交给rustc(rust编译器)来构建library或者binary

一个Package可以同时包含src/main.rs和src/lib.rs

一个Package也可以有多个binary create:

  • 文件放在src/bin,放在这里的每个文件都是单独的binary create

Create的作用

将相关功能组合到一个作用域内,便于在项目间进行共享。

同时,这也能防止命名冲突,例如rand create,访问它的功能需要通过它的名字:rand

定义module来控制作用域和私有性

Module:

  • 在一个create内,将代码进行分组
  • 增加可读性,易于复用
  • 控制项目(item)的私有性。public,private

建立module:

  • mod关键字
  • 可嵌套
  • 可包含其他项(struct、enum、常量、trait、函数等)的定义
mod front_of_house {mod hosting {fn add_to_waitlist() {}fn seat_at_table() {}}mod serving {fn take_order() {}fn serve_order() {}fn take_payment() {}}
} 

src/main.rs 和 src/lib.rs 叫做create roots:

  • 这两个文件(任意一个)的内容形成了名为create的模块,位于整个模块树的根部
  • 整个模块树在隐式的模块下

路径Path

路径的作用是为了在rust的模块中找到某个条目

路径的两种形式:

  • 绝对路径:从create root开始,使用create名或字面值create
  • 相对路径:从当前模块开始,使用self(本身),super(上一级)或当前模块的标识符

路径至少由一个标识符组成,标识符之间使用::

举个例子(下面这段程序将报错,我们将在后面讲到如何解决):

mod front_of_house {mod hosting {fn add_to_waitlist() {}}
}

pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//绝对路径front_of_house::hosting::add_to_waitlist();//相对路径
} 

那么为什么会报错呢?

我们查看报错的原因:module hosting is private,编译器告诉我们,hosting这个module是私有的。至此,为了解决这个问题,我们应该去了解一下私有边界

私有边界(private boundary)

  • 模块不仅可以组织代码,还可以定义私有边界
  • 如果把函数或struct等设为私有,可以将它放到某个模块中。
  • rust中所有的条目(函数,方法,struct,enum,模块,常量)默认情况下是私有的
  • 父级模块无法访问子模块中的私有条目
  • 但是在子模块中可以使用所有祖先模块中的条目

为什么rust默认这些条目是私有的呢?因为rust希望能够隐藏内部的实现细节,这样就会让开发者明确知道:更改哪些内部代码的时候,不会破坏外部的代码。同时,我们可以使用pub关键字将其声明为公共的。

pub关键字

rust默认这些条目为私有的,我们可以使用pub关键字来将某些条目标记为公共的。

我们将hosting声明pub,add_to_waitlist这个function也要声明pub

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}

pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//绝对路径front_of_house::hosting::add_to_waitlist();//相对路径
} 

为什么front_of_house这个mod不需要添加pub呢?因为它们是同级的。

super关键字

super:用来访问父级模块路径中的内容,类似文件系统中的..

fn serve_order() {}
mod front_of_house {fn fix_incorrect_order() {cook_order();super::serve_order();}fn cook_order() {}
} 

pub struct

声明一个公共的struct就是将pub放在struct前:

mod back_of_house {pub struct Breakfast {}
} 

声明了一个公共的struct后:

  • struct是公共的
  • struct的字段默认是私有的

而我们想让struct中的字段为公有的必须在前面加上pub

mod back_of_house {pub struct Breakfast {pub toast: String,//公有的seasonal_fruit: String, //私有的}
} 

也就是说:struct的字段需要单独设置pub来变成公有

我们看一个例子:

mod back_of_house {pub struct Breakfast {pub toast: String,//公有的seasonal_fruit: String, //私有的}impl Breakfast {//一个关联函数pub fn summer(toast: &str) -> Breakfast {Breakfast {toast: String::from(toast),seasonal_fruit: String::from("peaches"),}}}
}

pub fn eat_at_restaurant() {let mut meal = back_of_house::Breakfast::summer("Rye");meal.toast = String::from("Wheat");println!("I'd like {} toast please", meal.toast);meal.seasonal_fruit = String::from("blueberries");//报错:field `seasonal_fruit` is private
} 

pub enum

声明一个公共的enum就是将pub放在enum前:

mod back_of_house {pub enum Appetizer {}
} 

我们声明了一个公共的enum后:

  • enum是公共的
  • enum的变体也都是公共的
mod back_of_house {pub enum Appetizer {Soup,//公共的Salad, //公共的}
} 

为什么呢?因为枚举里面只有变体,只有变体是公共的这个枚举才有用。而struct中某些部分为私有的也不影响struct的使用,所以rust规定公共的struct中的字段默认为私有的。

Use关键字

我们可以使用use关键字将路径导入到作用域内,而我们引入的东西也任然遵循私有性规则(公共的引入的才能用)

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}fn some_function() {}//私有的,使用use导入后,外部依然不能调用这个函数}
}

use crate::front_of_house::hosting;
// 相当于mod hosting {}

pub fn eat_at_restaurant() {hosting::add_to_waitlist();hosting::add_to_waitlist();hosting::add_to_waitlist();
} 

使用use来指定相对路径(和使用条目时的规则相同):

use front_of_house::hosting; 

我们可以注意到我们调用的add_to_waitlist是导入的hostingmod下的,那我们可不可以直接导入function呢?

当然是可以的(不过并不推荐直接导入方法):

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}

use crate::front_of_house::hosting::add_to_waitlist;
// 相对于mod hosting {}

pub fn eat_at_restaurant() {add_to_waitlist();
} 

use的习惯用法

当我们直接导入方法时,我们有可能就搞不清楚是从其他模块导入的还是在这个作用域下声明的。

所以,通常情况下,我们导入的通常为父级模块。

//...
use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {hosting::add_to_waitlist();
} 

不过,struct,enum,其他:指定完整路径(指定到本身)

use std::collections::HashMap;
fn main() {let mut map = HashMap::new();map.insert(1, 2);
} 

但是同名的条目,我们在引入时需指定父级模块(比如下面的例子,两个类型都叫Result)

use std::fmt;
use std::io;

fn f1() -> fmt::Result {//...
}

fn f2() -> io::Result {//...
}
//... 

as关键字

关于上面同名的问题,还有另一种解决方法:使用as关键字

as关键字可以为引入的路径指定本地的别名

use std::fmt::Result;
use std::io::Result as IoResult;

fn f1() -> Result {//...
}

fn f2() -> IoResult {//...
} 

使用 pub use 重新导出名称

使用 use 将路径(名称)导入到作用域内后,该名称在此作用域内是私有的,外部的模块是没办法访问use导入的模块的。

由前面pub的作用可知,类似pub fn、pub mod,我们可以使用pub use来导入,相当于它导入了这个内容,然后又将它导出了。

(当我们使用pub use时会发现没有警告:“导入了但没有使用”,因为它同时也导出了,也被视作使用了这个导入的内容)

导入外部包

我们通过在Cargo.toml中的[dependencies]添加依赖:

# ...
[dependencies]
rand = "^0.8.5" 

出现:Blocking waiting for file lock on package cache

删除User/.cargo文件夹中的.package-cache文件。重新执行cargo build下载依赖。

很多时候我们的下载速度很慢,我们可以将下载源换到国内,在用户文件夹下的.cargo文件夹中添加 config 文件,写入以下内容:

[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
# 如果所处的环境中不允许使用 git 协议,可以把上面的地址改为
# registry = "https://mirrors.ustc.edu.cn/crates.io-index"
#[http]
#check-revoke = false 

这时候cargo build就会很快了。

我们这样导入:

use rand::Rng; 

另外:标准库也被当做外部包,需要导入,并且:

  • 我们不需要修改Cargo.toml来添加依赖
  • 需要使用use将std的特定条目导入到当前作用域

use多次导入(嵌套导入)

use std::{ascii, io};
//相当于:use std::ascii;
// use std::io; 

这样的导入该如何简写呢?

use std::io;
use std::io::Chain; 

可以使用self

use std::io::{self, Chain}; 

如何将模块放入其他文件?

假如我们的src/lib.rs中的内容是这样:

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
}
//... 

我们可以在lib.rs同级目录下新建front_of_house.rs,然后将模块内容写在文件中:

front_of_house.rs

pub mod hosting {pub fn add_to_waitlist() {}
} 

lib.rs

mod front_of_house;
//... 

如果我们想将hosting模块的内容单独存放呢?

我们需要新建一个front_of_house文件夹,并新建hosting.rs文件

hosting.rs

pub fn add_to_waitlist() {} 

front_of_house.rs

pub mod hosting; 

lib.rs

mod front_of_house;
//... 

原来的文件内容:

mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}}
} 

随着模块逐渐变大,这项功能将能够帮助我们更好的管理代码

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

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

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

相关文章

晒成绩单了,百度智能云交出2022年终大考试卷!

晒成绩单了,百度智能云交出2022年终大考试卷! 2023年伊始,工厂加快步伐复工复产、城市烟火气涌现、消费活力加速释放,企业对未来发展呈现乐观预期。有外媒称,“中国经济将实现比预期更快的复苏””。 站在更宏观的视…

java入门到废为止

目录基础数据变量类型数据类型基本类型上下转型引用类型类型对比装箱拆箱缓存池输入数据数组初始化元素访问内存分配数组异常二维数组运算参数形参实参可变参数方法方法概述定义调用注意事项方法重载重载介绍方法选取继承重载参数传递枚举Debug对象概述类定义构造器包封装thiss…

【React】二.JSX

目录 二.JSX JSX的基本使用 jsx使用步骤 JSX中使用JavaScript表达式 嵌入JS表达式 注意点 JSX的条件渲染 问题记录 JSX的列表渲染 JSX的样式处理 总结 二.JSX JSX的基本使用 createElement()的问题繁琐不简洁不能直观看出所描述的结构不优雅,用户体验不佳…

Java设计模式-代理模式Proxy

介绍 代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。 代理模式的主要作用是扩展目标对象的功能&a…

Linux编译器-gcc/g++的使用

📟作者主页:慢热的陕西人 🌴专栏链接:Linux 📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言 本博客主要内容主要介绍了Linux编译器g/gcc的相关使用方法&#xff0c…

Linux学习笔记——分布式内存计算Flink环境部署

5.13、分布式内存计算Flink环境部署 5.13.1、简介 Flink同Spark一样,是一款分布式内存计算引擎,可以支撑海量数据的分布式计算。 Flink在大数据体系同样是明星产品,作为最新一代的综合计算引擎,支持离线计算和实时计算。 在大…

libcurl库及curl API的简介

目录 一、libcurl简介 二、curl API简介 三.库安装编译方法 内容来源:Http协议之libcurl实现 - 谢呈勖 - 博客园 (cnblogs.com) 一、libcurl简介 libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。…

当 Rainbond 遇上龙蜥!小龙带你玩转一站式云原生,点击开启

Rainbond 是一个云原生应用管理平台,使用简单,不需要懂容器、Kubernetes 和底层复杂技术,支持管理多个 Kubernetes 集群,和管理企业应用全生命周期。主要功能包括应用开发环境、应用市场、微服务架构、应用交付、应用运维、应用级…

Golang的Fork/Join实现

做过Java开发的同学肯定知道,JDK7加入的Fork/Join是一个非常优秀的设计,到了JDK8,又结合并行流中进行了优化和增强,是一个非常好的工具。1、Fork/Join是什么Fork/Join本质上是一种任务分解,即:将一个很大的…

FPGA图像处理HLS实现RGB转灰度,提供HLS工程和vivado工程源码

目录一、图像RGB转灰度原理二、HLS方案实现三、HLS在线仿真并导出IP四、Kintex7开发板vivado工程验证五、zynq7100开发板vivado工程验证六、板级调试验证七、福利:工程源码获取一、图像RGB转灰度原理 图像rgb转灰度图有固定的公式,具体公式csdn一大堆&a…

mirco:bit是什么?小学生拿着它就能召唤神龙?

mirco:bit是什么?micro:bit是一款由英国广播电视公司(BBC) 为青少年编程教育设计,并由微软,三星,ARM,英国兰卡斯特大学等合作伙伴共同完成开发的微型电脑。BBC希望通过micro:bit驱动青少年参与到创造性的硬件制作和软件…

MySQL基础——DCL语句

概述 DCL(Data Control Language)语句:数据控制语句,用于控制不同数据段直接的许可和访问级别的语句。这些语句定义了数据库、表、字段、用户的访问权限和安全级别。 管理用户 查询 查询用户代码如下: USE mysql; SELECT * FROM user; …

ASEMI桥式整流器KBU808的优缺点

编辑-Z 型号:KBU808 最大重复峰值反向电压(VRRM):800V 最大RMS电桥输入电压(VRMS):560V 最大直流阻断电压(VDC):800V 最大平均正向整流输出电流&#xf…

3D视觉技术登上火星?NASA也用上了NeRF技术做太空勘探

原文链接:https://www.techbeat.net/article-info?id4468 作者:seven_ 现阶段,人类探索宇宙的一个关键方向是如何高效的利用航天器返回的数据来了解和分析外太空的环境特点。其中最为常用的就是图像数据,但是这些数据非常宝贵&am…

C语言-自定义类型-结构体应用-通讯录(11.2)

目录 1.通讯录的设计思路 1.1主函数与通讯录框架 1.2菜单的实现 1.3通讯录的定义与初始化 2.通讯录具体功能的实现 2.1添加联系人 2.2删除联系人 2.3查找联系人 2.4修改联系人信息 2.5整理通讯录(按年龄排序) 2.6查看整个通讯录 3.通讯录源码…

Ubuntu 网络管理

一:NetPlan配置 1、安装netplan 如果/etc/netplan目录不存在请用以下命令安装: apt -y install netplan.io 2、配置文件 创建并编辑/etc/netplan/01-netplan.yaml文件: eth0:动态分配;eth1:静态分配 …

聊一聊nginx中KeepAlive的设置

文章目录问题分析为什么要有KeepAlive?TCP KeepAlive和HTTP的Keep-Alive是一样的吗?Nginx的TCP KeepAlive如何设置Apache中KeepAlive和KeepAliveTimeOut参考资料问题 之前工作中遇到一个KeepAlive的问题,现在把它记录下来,场景是…

【BP靶场portswigger-客户端11】跨站点脚本XSS-20个实验(上)

前言: 介绍: 博主:网络安全领域狂热爱好者(承诺在CSDN永久无偿分享文章)。 殊荣:CSDN网络安全领域优质创作者,2022年双十一业务安全保卫战-某厂第一名,某厂特邀数字业务安全研究员&…

ssm权限管理系统1

先说一个声明,这个文章可能不完整,也就是说,我只是列举出了项目里面部分疑问难点,然后你们想做,可以去b站查看这个项目的视频。我这里会上传每一部分源代码 svn: 安装好svn服务器之后,我们需要去服务器建…

Python之勒让德多项式

文章目录勒让德多项式简介求导和积分求根和反演拟合勒让德多项式简介 Legendre多项式是一种非常重要的正交多项式,在物理学中有着广泛的应用,例如点电荷在空间中的激发电势就具备勒让德多项式的形式。其表达形式为 Pn(x)12nn!dndxn{(x2−1)n}P_n(x)\fra…