[自用]Rust速通day5:包、crate和use

模块

包和Crate

首先介绍crate,由于Rust官方文档讲述过于晦涩,我只讲述自己的理解:

crate本身是一个抽象的概念,可以理解成是实现特定功能的集合,有多个.rs文件组成。

crate分为两种:二进制crate和库crate,

二进制crate中必须包含一个main函数来定义程序被执行的时候需要做的事,可以被编译成可执行程序,一个包内有可以有多个二进制crate。

而库crate则是实现功能并提供api接口,提供了能给多个项目复用的功能,一个包内只能有一个。

一个包内至少包含一个crate(不管是库还是二进制)。

是crate构建的入口,在运行指令cargo build之后,会从根开始构建起整个模块树

接下来会用一个例子来详细说明:

我现在需要构造一个智能家具系统,文件结构如下:

复制代码
smart_home/                      # 包根目录
├── Cargo.toml                   # 包配置文件
└── src/                         # 源代码目录
    ├── lib.rs                    # 库 crate 的根
    ├── device.rs                 # 库 crate:设备通用接口
    ├── temperature.rs            # 库 crate:温度传感器相关功能
    ├── logger.rs                 # 库 crate:日志功能
    ├── main.rs                   # 二进制 crate "smart_home" 的根
    ├── ac_control.rs             # 二进制 crate "smart_home" 的私有模块:空调控制
    ├── fridge_control.rs         # 二进制 crate "smart_home" 的私有模块:冰箱控制
    └── bin/                      # 存放其他二进制 crate 的根文件
        ├── mobile_app.rs          # 二进制 crate "mobile_app" 的根
        ├── mobile_app/            # mobile_app 的私有模块目录
        │   ├── ui.rs              # mobile_app 的私有模块:界面
        │   └── notify.rs          # mobile_app 的私有模块:通知
        ├── web_dashboard.rs       # 二进制 crate "web_dashboard" 的根
        └── web_dashboard/         # web_dashboard 的私有模块目录
            ├── charts.rs          # web_dashboard 的私有模块:图表
            └── auth.rs            # web_dashboard 的私有模块:认证

里面有三个二进制crate:smart_homemobile_appweb_dashboard,分别负责智能家居的总调控,手机app功能实现,网页功能实现,并且构建了库crate,包含多个模块文件。每个crate都有自己的根。(通常main.rs是主程序的根,lib.rs是库crate的根,其他二进制crate的根统一储存在文件夹src/bin/内,并且每一个src/bin下的文件都会被编译成一个独立的二进制crate)

smart_home以根main.rs开始,再通过文件内的mod声明查找对应的文件并纳入模块树中,类似于:

rust 复制代码
// src/main.rs

mod ac_control  // 告诉编译器存在一个 ac_control 模块,它将在 ac_control.rs 或 ac_control/mod.rs 中定义
mod fridge_control
mod device //引入 库crate 的 device 模块

这些模块共同构成了一个实现家具控制的功能集合,也就是二进制crate。

在实际的实现过程中,如果一个模块(比如目录中的device文件)同时被另一个二进制 crate 通过 mod 引入,那么它们会在那个二进制 crate 中被再次编译,形成两份独立的代码副本。这可不是我们想要看到的。

因此我们在二进制crate之外还有库crate,能让我们把共享代码放在库crate中,然后让二进制 crate 通过依赖来使用,而不是直接复制模块文件,从而实现功能的复用

use将路径引入作用域

use的作用可以理解为是快捷方式,通过创建一个快捷方式把特定的模块导入到"桌面",也就是作用域中。这样我们就不用写繁琐的前缀了,可以直接使用。例如以下代码:

rust 复制代码
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

如果没有use的话,我们就需要写crate::front_of_house::hosting::add_to_waitlist()

你可以理解成他其实是在crate的作用域中创建了一个虚拟的mod hosting

但是注意use只能创建use所在的特定作用域的捷径,也就是说,如果把eat_at_restaurant()移动到子模块customer中,就到了另一个作用域,因此无法访问hosting::add_to_waitlist()

解决方法是在子模块中重新使用use构造捷径。就像这样:

rust 复制代码
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
use crate::front_of_house::hosting;
mod customer {
    pub fn eat_at_restaurant() {
        use crate::front_of_house::hosting;
        hosting::add_to_waitlist();
    }
}

另一个解决方法是使用pub use重新导出,这样子导出的模块会暴露在所有作用域下:

rust 复制代码
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
pub use crate::front_of_house::hosting;
mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }
}

当你代码的内部结构与调用你代码的程序员所想象的结构不同时,重导出会很有用。例如,在这个餐馆的比喻中,经营餐馆的人会想到"前台"和"后台"。但顾客在光顾一家餐馆时,可能不会以这些术语来考虑餐馆的各个部分。使用 pub use,我们可以使用一种结构编写代码,却将 不同的结构形式暴露出来。这样做使我们的库井井有条,也使开发这个库的程序员和调用这个库的程序员都更加方便。

你可能会有这样的疑惑:如果我要用函数add_to_waitlist()的话,为什么我不用use crate::front_of_house::hosting::add_to_waitlist()直达,然后直接使用函数呢?

原因是因为这不符合习惯,上面展示的写法可以清晰地区分函数是不是在本地定义的 ,同时也可以让路径的重复度减小。另一方面,使用use引入结构体、枚举以及其他项的时候,习惯是指定他们的完整路径,比如以下把HashMap结构体引入二进制crate作用域的做法:

rust 复制代码
use std::collections::HashMap;
fn main() {
    let mut map = HashMap::new();
    map.insert(1, 2);
}
相关推荐
沛沛rh455 分钟前
力扣 42. 接雨水 - 高效双指针解法(Rust实现)详细题解
算法·leetcode·rust
pan30350747930 分钟前
在 Vue 3 + Vite 项目中覆盖 Element Plus 的默认样式
前端·vue.js·rust
Rust研习社1 小时前
Rust 的构建脚本是什么?今天一次性搞懂它
rust
向上的车轮18 小时前
从零实现一个高性能 HTTP 服务器:深入理解 Tokio 异步运行时与 Pin 机制
rust·系统编程·pin·异步编程·tokio·http服务器
AI自动化工坊1 天前
OpenFang实战指南:用Rust构建高并发AI Agent操作系统
开发语言·人工智能·ai·rust·agent·ai agent
gsls2008081 天前
tauri开发环境搭建
rust·npm·tauri
Binarydog_Lee1 天前
Tauri2 开发入门:应用是如何启动的
前端·rust·tauri
changzehai1 天前
RustRover + J-Link 一键调试 STM32 教程
stm32·单片机·嵌入式硬件·rust·rustrover
咸甜适中1 天前
rust序列化和反序列化(json、yaml、toml)详解
开发语言·rust·json
IT 行者1 天前
CentOS 下源码编译安装完整版 Redis 8.0 指南(附 Rust 工具链详解)
redis·rust·centos