[自用]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);
}
相关推荐
tedcloud1234 小时前
OpenLogi 怎么搭建?用 Rust 打造一个轻量的 Logitech 外设管理工具
linux·运维·服务器·开发语言·后端·rust·开源
传奇开心果编程5 小时前
【Rust入门知识点学与练】第5课:函数
开发语言·学习·rust
传奇开心果编程6 小时前
【Rust入门知识点学与练】第9课:Vec 动态数组
开发语言·学习·rust
yume_sibai6 小时前
02-Rust 所有权与借用深入解析(底层原理 + 借用检查器 + 生命周期 + 内部可变性)
开发语言·后端·rust
传奇开心果编程6 小时前
【Rust入门知识点学与练】第1课变量与打印
开发语言·学习·rust
梦醒沉醉7 小时前
std1.97.1——default模块和clone模块总览
rust
小灰灰搞电子1 天前
Rust std::vec::Vec<T>动态数组:所有相关 API 全面介绍
开发语言·rust
学习星球1 天前
CodeWhale 深度剖析:从 DeepSeek-TUI 到 40.9K Star 的 Rust 终端编程 Agent
开发语言·后端·rust
小灰灰搞电子1 天前
Rust 相关容器(集合)详解
开发语言·容器·rust