[自用]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);
}
相关推荐
花褪残红青杏小9 小时前
Rust图像处理第6节- 均值模糊 & 中值模糊:3×3 邻域的两种经典玩法
rust·webassembly·图形学
子兮曰13 小时前
前端工具链的「Rust 化」:一场没有赢家的军备竞赛?
前端·后端·rust
星栈15 小时前
写 Dioxus Demo 不难,难的是把它写成项目
前端·rust·前端框架
mCell17 小时前
【锐评】桌面端技术营销:别拿跑分当工程判断
前端·rust·electron
武子康21 小时前
调查研究-201 Rust 里的 dev build 和 release build:为什么同一份代码性能差这么多?
后端·架构·rust
doiito1 天前
【Agent Harness】Gliding Horse 的 L2 作战地图:让多 Agent 协作从“摸黑”变成“透明”
ai·rust·架构设计·系统设计·ai agent
星栈2 天前
我用 Rust + Dioxus 做了个全栈跨平台笔记应用:再把新建、编辑和交付补上
前端·rust·前端框架
独孤留白2 天前
从C到Rust:基本类型 C 的隐式不确定 vs Rust 的显式确定
rust
清晨很温柔啊2 天前
# 用 Rust 手搓 AI 自演化主板:当 18 个异构器官长出 C++ 骨骼
rust
星栈3 天前
我用 Rust + Dioxus 做了个全栈跨平台笔记应用:第一版先把列表和详情跑通
前端·rust·前端框架