Rust中的包(packages)、模块(modules)和箱(crates)的爱恨情仇

开发 前端
在 Rust 中,一个重要的概念是 crate 根(crate root)。crate 根是编译器构建程序的起点。在上面的例子中,无论我们将文件命名为 something.rs 还是其他名字,这个文件都会被视为 crate 根。

今天,我想和大家分享我对 Rust 中的包(packages)、模块(modules)和箱(crates)的理解。Rust 的组织系统一开始让我觉得很难掌握,但经过一段时间的学习和实践,我终于逐渐理清了它们的关系。

让我们从基础开始,逐步深入!

箱(Crates)

箱(crate)是 Rust 程序的最小单元。例如,下面这段代码就是一个简单的 crate:

fn main() {
    println!("I am a crate.");
}

在 Rust 中,一个重要的概念是 crate 根(crate root)。crate 根是编译器构建程序的起点。在上面的例子中,无论我们将文件命名为 something.rs 还是其他名字,这个文件都会被视为 crate 根。

Rust 中有两种类型的 crate:二进制 crate 和 库 crate。

  • 二进制 crate 是独立的可执行文件,包含一个 main 函数,可以直接运行。
  • 库 crate 是一组功能的集合,供其他 crate 使用。它没有 main 函数,无法独立运行。

在组织 Rust 程序时,一个常见的方式是将程序拆分为一个二进制 crate 和一个库 crate。二进制 crate(通常命名为 main.rs)包含可执行文件,而库 crate 则存储可复用的功能。二进制 crate 可以通过导入库 crate 的类型、方法等来使用它的功能。

包(Packages)

包(package)是一个或多个 crate 的集合,它们协同工作以提供某种功能。每个包都包含一个 Cargo.toml 文件,用于告诉 Rust 编译器如何构建其中的 crate。

  • 一个包可以包含多个二进制 crate,但只能包含一个库 crate。
  • 至少,一个包必须包含一个 crate(无论是二进制还是库)。

项目根目录下的 Cargo.toml 文件定义了一个包的存在。默认情况下,Cargo 会假定 src/main.rs 是二进制 crate 的根,而 src/lib.rs 是库 crate 的根。包的名称默认与二进制或库 crate 的名称一致,但可以在 Cargo.toml 中自定义。例如:

[[bin]]
name = "fun-with-nom"
path = "src/bin/httpd.rs"

[lib]
name = "fun_with_nom_lib"
path = "src/lib/lib.rs"

在组织 Rust 项目时,我发现将初始化和启动逻辑放在一个二进制 crate 中,而将核心功能放在一个库 crate 中非常有用。对于小型项目,这种结构可能显得繁琐,但对于大型代码库(例如 API),这种模块化的结构可以显著提高代码的可维护性。遵循这种结构让我在回顾旧项目时省了不少麻烦。

模块(Modules)

crate 可以进一步划分为模块(module),模块可以存在于单个文件中,也可以分布在多个文件中。模块的主要作用有两个:

  • 组织代码:将相关代码分组为易于管理的单元。
  • 控制可见性:模块中的代码默认是私有的,除非显式声明为公共(public)。

虽然可以将所有模块定义在一个文件中,但这种方式很快会变得难以管理。因此,将模块组织到单独的文件中是一种更好的做法,便于导航和维护。

路径(Paths)

Rust 编译器使用路径(path)来定位代码。路径类似于 Windows、Linux 或 macOS 中的文件系统路径,分为两种形式:

  • 绝对路径:从 crate 根开始。对于外部 crate,路径以 crate 名称开头;对于当前 crate 的代码,路径以 crate 关键字开头。
  • 相对路径:从当前模块开始,使用 self、super 或当前模块中的标识符。例如,super 表示父模块。

use 关键字

use 关键字可以将模块引入作用域,使其内容可以在程序的其他部分访问。这在避免重复书写路径时尤其有用。例如:

use serde::Deserialize;

如果我们在 Cargo.toml 的依赖项中添加了 serde crate(包括 derive 功能标志),这行代码会将 Deserialize 宏引入作用域,以便我们可以在自定义类型中使用它。

命名空间操作符(Namespace Operator)

Rust 的命名空间操作符 :: 通常与 use 关键字一起使用,用于访问模块中的项。例如:

use axum::{http::StatusCode, routing::get, response::IntoResponse};

在这里,我们引入了 axum Web 应用框架,并同时引入了以下具体依赖:

  • http 模块中的 StatusCode 类型
  • routing 模块中的 get 方法
  • response 模块中的 IntoResponse 特性(trait)

当我们只需要模块中的某些内容时,可以将它们用 {} 包裹起来。

总结

一开始,我对如何有效使用 Rust 中的 crate、package 和 module 感到困惑。但随着实践的积累,这些概念逐渐变得清晰并融会贯通。我希望这篇文章能帮助那些面临类似挑战的读者。

责任编辑:武晓燕 来源: Rust开发笔记
相关推荐

2025-01-03 09:39:04

2022-09-02 12:13:22

TCPUDP场景

2019-05-15 15:10:12

Tomcat Session Cookie

2022-05-13 09:47:28

Docker容器

2021-04-12 06:08:16

HiveSpark大数据

2020-11-24 10:13:20

测试开发管理

2024-08-07 08:22:27

2024-06-05 11:06:22

Go语言工具

2024-06-07 00:09:50

2020-04-09 15:26:55

间谍软件NSOFacebook

2022-05-07 07:43:07

Redis存储系统数据库

2013-02-20 10:00:16

微软CodePlexGitHub

2021-06-16 06:48:06

接口微信

2024-03-11 09:37:01

模型图片编辑

2020-06-16 15:48:40

苹果英特尔芯片

2015-11-24 15:13:15

2015-11-05 09:55:40

SDNNFV

2020-05-27 14:07:21

蜂窝广域网局域物联网物联网

2017-01-10 09:59:51

2023-10-24 16:03:34

GoGolang
点赞
收藏

51CTO技术栈公众号