8.使用包、crate和模块

news2024/11/25 16:37:45

目录

  • 一、简单概念
  • 二、crate和包
    • 2.1 crate规则
    • 2.2 包规则
    • 2.3 Cargo的遵循的一些约定
    • 2.4 控制模块的作用域和私有性
      • 1) 模块
      • 2)引用模块树中的项
      • 3)使用 super 起始的相对路径
      • 4) 公有结构体和枚举
  • 三、use关键字的使用
  • 四、分割模块进入不同的文件

一、简单概念

Rust 有许多功能可以管理代码的组织,包括哪些内容可以被公开,哪些内容作为私有部分,以及程序每个作用域中的名字。这有时被称为 模块系统(the module system),包括:

  • 包(Packages): Cargo 的一个功能,它允许构建、测试和分享 crate
  • Crates: 一个模块的树形结构,它形成了库或二进制项目;
  • 模块(Modules)和 use: 允许控制作用域和路径的私有性
  • 路径(path):一个命名例如结构体、函数或模块等项的方式;

二、crate和包

2.1 crate规则

  • crate 是一个二进制项或者库
  • crate root是一个源文件,Rust 编译器以它为起始点构成 crate 的根模块;
  • 包(package)是提供一系列功能的一个或者多个crate
  • 一个包会包含一个Cargo.toml文件

2.2 包规则

  • 一个包中至多只能包含一个库crate(library crate);
  • 包中可以包含任意多个二进制 crate(binary crate);
  • 包中至少包含一个crate,无论是库的还是二进制的;

2.3 Cargo的遵循的一些约定

  • 创建项目时,src/main.rs 就是一个与包同名的二进制crate的crate根;
  • 如果包目录中同时包含src/lib.rs,则有两个crate(一个库,一个二进制),且crate的名字都与包名相同;
  • 将文件放在src/bin 目录下,一个包可以拥有多个二进制crate,目录下的每个文件都会被编译成一个独立的二进制 crate;
  • 一个crate会将一个作用域内的相关功能分组到一起,这样就可以很方便地在多个项目之间共享;

2.4 控制模块的作用域和私有性

1) 模块

  • 模块可以让我们将一个crate中的代码进行分组,以提高可读性与重用性;
  • 模块还可以控制项的私有性,即项是可以被外部代码使用的(public),还是作为一个内部实现的内容,不能被外部代码使用(private);
  • 模块可以包含模块,也可以包含结构体、枚举、常量、trait或者函数;

下面的模块定义了酒店前台,其中有两个子模块,分别为hostingserving。新建一个src/lib.rs文件,将下面的代码粘贴进去。

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被称为crate根,这两个文件中任意一个的内容会构成名为crate的模块,且该模块位于crate的被称为模块树的模块结构的根部;
  • 结构如下
crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment
  • 最顶部的crate模块是隐式存在的;
  • 结构展示了模块之间的嵌套关系(hosting在front_of_house内部)以及兄弟关系(hosting与serving)

2)引用模块树中的项

路径有两种形式:
绝对路径: 从 crate 根部开始,以crate名或者字面量 crate 开头;
相对路径: 从当前模块开始,以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();
}
  • crate是隐式的顶层模块,因此绝对路径从crate开始
  • add_to_waitlist与eat_at_restaurant在同一crate下,因此相对路径从当前文件下的front_of_house开始;
  • 相对路径与绝对路径的使用依据具体的项目确定,但由于把代码定义和项调用各自独立地移动是更常见的,因此更倾向于使用绝对路径

编译后发现编译不过在这里插入图片描述

  • 报错信息说hosting模块是私有的,因此模块内部默认都是私有的
  • 事实上,Rust中默认所有项(函数、方法、结构体、枚举、模块和常量)都是私有的;
  • 由于子模块封装并隐藏了他们的实现详情,但是子模块可以看到他们定义的上下文,因此父模块中的项不能使用子模块中的私有项,但是子模块中的项可以使用父模块中的项;
  • 子模块可以通过使用pub关键字创建公共项,使子模块的内部部分暴露给上级模块;
  • 添加pub关键字的模块并不使其内容也是公有的,模块上的 pub 关键字只允许其父模块引用它;

将hosting改为公有

mod front_of_house {
    pub 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();
}

编译之后发现add_to_waitlist并非公有。
在这里插入图片描述
因此,将add_to_waitlist()也变成公有的,外部才能访问

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();
}
  1. 私有性规则不但应用于模块,还应用于结构体、枚举、函数和方法。
  2. eat_at_restaurant和front_of_house在同一个文件里都属于根级,属于根级的可以互相调用而无论是公有的还是私有的

3)使用 super 起始的相对路径

  • 可以使用super开头来构建从父模块开始的相对路径;
  • fix_incorrect_order函数在back_of_house模块中,所以可以使用super进入back_of_house父模块(即顶层crate)
fn serve_order(){}

mod back_of_house {
    fn fix_incorrect_order(){
        cook_order();
        super::serve_order(); //上级路径(模块外)下的serve_order
        crate::serve_order(); //绝对路径访问
    }

    fn cook_order(){}
}

4) 公有结构体和枚举

  • 可以使用pub来定义公有结构体和枚举;
  • 定义为pub的结构体,其字段仍然是私有的,需要根据情况决定每个字段是否公有;
  • 将枚举定义为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"),
           }
       }
   }
}

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");
}
fn main(){
   eat_at_restaurant();
}
  • 代码中Breakfast结构体以及内部的toast为公有的,季节水果字段seasonal_fruit是私有的,不允许被外部修改
  • 由于back_of_house::Breakfast具有私有字段,所以这个结构体需要提供一个关联函数来构造实例;
  • 如果没有这样的函数,由于不能在eat_at_restaurant中设置私有字段seasonal_fruit的值,因此无法在eat_at_restaurant中创建Breakfast实例;

枚举的公有用法

mod back_of_house {
    pub enum Appetizer {
        Soup,
        Salad,
    }
}

pub fn eat_at_restaurant() {
    let order1 = back_of_house::Appetizer::Soup;
    let order2 = back_of_house::Appetizer::Salad;
}

三、use关键字的使用

使用 use 关键字将路径一次性引入作用域

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

use crate::front_of_house::hosting;
// use front_of_house::hosting;           //可以使用相对路径

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::other_function();
}
  • 使用use之后可以简写eat_at_restaurant中对于hosting模块内部函数的调用;
  • 通过use引入作用域的路径依然会检查私有性;

use的习惯用法

  • 将函数的父级模块引入作用域时通常指定到父级,这样可以清晰的表明函数不是在本地定义的
  • struct、enum等引入作用域时通常指定完整路径(指定到本身)
  • 同名条目:指定到父级
use std::io;
use std::fmt;
use std::collections::HashMap;

//返回不同的Result
fn function1() -> fmt::Result {
    // --snip--
}

fn function2() -> io::Result<()> {
    // --snip--
}

fn main() {
    let mut map = HashMap::new();  //明确表示是外部引入的HashMap
    map.insert(1, 2);
}

使用use关键字定义别名

在这个类型的路径后面,可以使用as关键字指定一个新的本地名称或者别名

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

fn function1() -> Result {
    // --snip--
}

fn function2() -> IoResult<()> {
    // --snip--
}

使用pub use重导出名称

  • 使用use将路径(名称)导入到作用域内后,该名称在此作用域内是私有的;
  • 为了让调用此代码的代码能在自己的作用域中引用这些类型,需要使用pub use进行重导出(re-exporting)
mod front_of_house{
    pub mod hosting{
        pub fn add_to_waitlist(){}
    }
}

pub use crate::front_of_house::hosting;  //加上pub后,外部也可以看到这个hosting了

pub fn eat_at_restaurant(){
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}
  • 加上pub use后,可以通过新路径hosting::add_to_waitlist来调用add_to_waitlist函数;
  • 如果没加,则外部模块不能使用这个新路径;

使用外部包

比如要使用rand合成随机数,则在Cargo.toml文件中加入

[dependencies]
rand = "0.8.3"

使用

use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1..101);
}

路径嵌套

  1. 下面的代码可以写成use std::{cmp::Ordering, io, collections::HashMap};
use std::collections::HashMap;
use std::io;
use std::cmp::Ordering;
  1. 下面的代码可以写成use std::io::{self, Write};
use std::io;
use std::io::Write;
  1. 可以使用"*"运算符引入路径下的所有公有项
use std::collections::*;
  • 这通常用于测试模块tests中;
  • 一般用于预导入(prelude)模式;

四、分割模块进入不同的文件

文件src/lib.rs的内容为

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

use crate::front_of_house::hosting;

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

如果变成

mod front_of_house;

use crate::front_of_house::hosting;

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

则需要以下步骤

  1. 创建模块front_of_house的同名文件名front_of_house.rs,内容为
pub mod hosting {
    pub fn add_to_waitlist() {}
    pub fn other_function(){}
}

即可。

  1. 如果要更进一步将front_of_house.rs文件的内容改为pub mod hosting;,则还需创建src/front_of_house/hosting.rs文件,内容为
pub fn add_to_waitlist() {}
pub fn other_function() {}
  1. 只做到第1步比较简单,这里贴一下第二步的目录结构
    在这里插入图片描述
    各个文件的值如下
//lib.rs文件内容
mod front_of_house;

use crate::front_of_house::hosting;

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

//front_of_house.rs文件内容
pub mod hosting;

//hosting.rs文件内容
pub fn add_to_waitlist(){}
pub fn other_function(){}

最复杂的这种分割方法保留了原本的模块树结构

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

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

相关文章

Vue27-内置指令04:v-once指令

一、需求 二、v-once指令 获取初始值&#xff1a; 三、小结

HCIA6以太网基础基于MAC划分VLAN

&#xff08;简写的命令可以敲Tab按键补全剩余&#xff09; 1.组网需求 场景&#xff1a;公司的网络中&#xff0c;管理者将同一部门的员工划分到VLAN10。要求只有本部门员工的PC接入才能互访&#xff0c;其他PC接入交换机属于其他VLAN&#xff08;666&#xff09;。可以配置…

STM32自己从零开始实操05:接口电路原理图

一、TTL 转 USB 驱动电路设计 1.1指路 延续使用芯片 CH340E 。 实物图 原理图与封装图 1.2数据手册重要信息提炼 1.2.1概述 CH340 是一个 USB 总线的转接芯片&#xff0c;实现 USB 与串口之间的相互转化。 1.2.2特点 支持常用的 MODEM 联络信号 RTS&#xff08;请求发送&…

python实战根据excel的文件名称这一列的内容,找到电脑D盘的下所对应的文件位置,要求用程序实现

今天客户需要 根据excel的文件名称这一列的内容&#xff0c;找到电脑D盘的下所对应的文件位置&#xff0c;要求用程序实现 数据样例&#xff1a;记录.xlsx 解决代码&#xff1a; 1、安装必要的库&#xff1a; pip install pandas openpyxl2、编写Python脚本&#xff1a; im…

基于springboot实现高校专业实习管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现高校专业实习管理系统的设计演示 摘要 随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的高校专业实…

RabbitMQ配置与交换机学习

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

OpenCV学习(4.11) OpenCV中的图像转换

1. 目标 在本节中&#xff0c;我们将学习 使用OpenCV查找图像的傅立叶变换利用Numpy中可用的FFT功能傅立叶变换的一些应用我们将看到以下函数&#xff1a;**cv.dft()** &#xff0c;**cv.idft()** 等 理论 傅立叶变换用于分析各种滤波器的频率特性。对于图像&#xff0c;使用…

elementPlus 图标不显示 属性模式不显示

问题&#xff1a; elementPlus 属性模式图标不显示 <el-input placeholder"请输入用户名" :suffix-icon"Avatar"> //这个图标不显示 之前在main.ts里全局引入了icons-vue。这里的script里也没引入。 解决&#xff1a; 在当前的script中重新引入a…

MS721仪表总线(M-Bus)从站收发电路

MS721 是为 M-Bus 标准 (EN1434-3) 的应用而开发的单片收发 电路。 MS721 接口电路可以适应从站与主站之间的电压差&#xff0c;总 线的连接没有极性要求&#xff0c;电路由主站通过总线供电&#xff0c;这样从站 电池就不会增加额外的负载&#xff0c;同时还集成电源失效功…

Webshell-jsp 冰蝎流量

考点:冰蝎jsp流量解密crc碰撞png暴力爆破宽高 主要是和 main.jsp进行通信浅浅看一下 yjg.txt内容 用jsp写的一些脚本 过滤http流量 可以判断是 冰蝎 的jsp webshell 尝试爆破常用密钥 无果 那么 密钥一定在流量中 看冰蝎动态生成密钥的最后一个返回包 就是明文的key 尝试多试…

Mac外接显示器显示不全

Mac外接显示器最大化无法占满屏幕 当你遇到底部无法占满的时候&#xff0c; 只需要&#xff0c;在-->系统设置 中修改“桌面与程序坞”-->“置于屏幕上的位置”&#xff0c;改完之后&#xff0c;改回原设置就行了&#xff1b;

vue2+echarts实现简易的2d地图效果

背景 公司的一个可视化数据大屏里面&#xff0c;有一个使用echarts实现的2d地图。不是我开发的&#xff0c;在此记录一下实现过程以及一些扩展解构。应该是从哪个航空航线图改动了一下&#xff0c;效果看起来还是可以的。 效果预览 版本 vue版本使用的是"^2.6.12"…

基于springboot高校就业招聘系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;就业咨询管理&#xff0c;毕业去向管理&#xff0c;简历管理&#xff0c;管理员管理&#xff0c;基础数据管理 辅导员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;就业咨询管理…

函数递归(C语言)(详细过程!)

函数递归 一. 递归是什么1.1 递归的思想1.2 递归的限制条件 二. 递归举例2.1 求n的阶乘2.2 按顺序打印一个整数的每一位 三. 递归与迭代3.1 求第n个斐波那契数 一. 递归是什么 递归是学习C语言很重要的一个知识&#xff0c;递归就是函数自己调用自己&#xff0c;是一种解决问题…

【消息中间件】Pulsar 基本知识

文章目录 Pulsar一、主要特性二、应用场景三、架构设计四、Topic分区Topic&#xff08;Topic-Partition&#xff09;非持久Topic重试Topic死信Topic 五、通用的消费模型队列模型流模型Pulsar既支持队列模型&#xff0c;也支持流模型 六、订阅模式独占模式&#xff08;流模型&am…

作业8:信息存储的层次与并行技术

作业8&#xff1a;信息存储的层次与并行技术 一. 单选题&#xff08;共7题&#xff0c;70分&#xff09; (单选题) 考虑为以下表达式生成代码 A&#xff1d;B&#xff0b;C &#xff1b; D&#xff1d;E&#xff0d;F &#xff1b; 在执行过程中不需要插入任何停顿周期就能够消…

Hive函数学习之UDTF与UDAF的应用案例

UDTF与UDAF的应用案例&#xff1a; UDTF&#xff1a;一进多出 UDTF是一对多的输入输出,实现UDTF需要完成下面步骤 M1001#xiaohu#S324231212,lkd#M1002#S2543412432,S21312312412#M1003#bfy 1001 xiaohu 324231212 1002 lkd 2543412432 1003 bfy 21312312412 继承org.apache.ha…

partially initialized module ‘charset_normalizer‘ has no attribute ‘md__mypyc‘

django项目运行报错&#xff1a; partially initialized module ‘charset_normalizer‘ has no attribute ‘md__mypyc‘…… 解决办法 pip install --force-reinstall charset-normalizer3.1.0

R3CTF 2024 取证部分

TPA 02 - &#x1f4f1; Peggy 是一家公司的员工&#xff0c;和许多人一样&#xff0c;她偶尔也会使用个人手机执行与工作相关的任务。不幸的是&#xff0c;她成为了网络钓鱼攻击的目标。你的任务是找出攻击者的电话号码和佩吉的密码&#xff0c;从而揭露这次攻击的细节。 以…

为什么电容两端电压不能突变

我们先从RC延时电路说起吧&#xff0c;图1是最简单的RC延时电路&#xff0c;给一个阶跃的电压信号&#xff0c;电压会变成黄色曲线这个样子&#xff0c;这是为什么呢&#xff1f; 图1 电压跳变后&#xff0c;电源负极电子移动到电容下极板&#xff0c;排斥上极板电子流动到电源…