如何组织大型 Rust 代码库

随着 Rust 项目规模的扩大,需要付出不少努力才能保持其清洁状态,不会妨碍您和您的同事的工作效率。

这就是为什么我决定分享我用来管理大型 Rust 代码库并避免复杂性和技术债务的简单规则。

要牢记的最终目标是,任何使用您的代码库的人都应该能够打开根文件夹并感到轻松自在:一切都应该“各归其位”。

1、提供开发容器
我非常喜欢沙盒和不可变的开发环境,这就是为什么我的第一条规则是您应该至少提供Dockerfile构建和调试代码库所需的所有依赖项。

理想情况下,您应该提供一个开发容器,以便任何使用兼容代码编辑器的人都可以立即开始处理您的代码库。

2、使用工作区
随着项目的发展,您需要将其拆分为多个包,以减少编译时间并保持代码整洁。为此,Rust 提供了Cargo WorkspacesCargo.lock ,它们是共享相同文件和目录的包集target。

我的典型工作空间的组织方式如下:

$ ls my_project
cmd/
    executable-1/
    executable-2/
crates/
    package_1/
    package_2/
    package_3/
Cargo.toml
Cargo.lock
target/

以根目录为工作区的Cargo.toml文件。

文件夹中包含项目的cmd不同可执行文件(带有文件的包),例如,等等。main.rsproject-serverproject-worker

crates文件夹(或者,文件夹)中包含libs所有库、实用程序和其他非可执行文件。

最后,还可以有几个辅助文件夹,例如scripts提供各种支持和维护脚本或docker包含要嵌入 Docker 镜像的资产。

3、在工作区级别声明依赖项
随着您的项目发展到十几个包,您不可能让所有包的依赖项保持同步。这就是为什么您应该在工作空间中声明依赖项( version, ...)并在包中使用它们。featuresCargo.tomlxx = { workspace = true }

Cargo.toml

[workspace.dependencies]
something = { version = "=1.0.0", features = ["a", "b", "c"] }

<strong>...

在您的工作区成员中:

crates/package1/Cargo.toml

[dependencies]
something = { workspace = true }

...
它还有助于保存项目中使用的所有第三方包及其版本的清单。

4、不要使用 Cargo 的默认文件夹结构
默认情况下,cargo使用以下文件夹结构:

my_package/
    src/
        my_package.rs
    Cargo.toml
    README.md

但是,随着包数量的增加,src一遍又一遍地打开所有这些子文件夹来访问代码很快就会变得令人厌烦。

这就是为什么我建议“扁平化”你的包并将代码直接放在包的根文件夹中,与文件一起Cargo.toml:

my_package/
    my_package.rs
    Cargo.toml
    README.md

Cargo.toml

<strong>...</strong>

[lib]
path = "./my_package.rs"

您可以在deno's codebase.中看到它的实际运行。

此规则的唯一例外是如果您有文件build.rs。在这种情况下,最好保留cargo的标准布局。

my_package/
    src/
        my_package.rs
    build.rs
    Cargo.toml
    README.md

5、不要在mod.rsandlib.rs文件中放入任何代码
正如我之前所写mod.rs,你应该避免在和文件中放置任何代码lib.rs。这些文件应该专门用于调整不同类型和函数的可见性,而应该使用描述性文件名(client.rs,,email.rs... payments.rs)来命名你的代码。

为什么?
Search file因为随着您的项目不断增长,包含数十到数百个包和数千个模块,当所有内容都隐藏在数千个不同的文件中时,使用代码编辑器的功能mod.rs将无法找到任何代码lib.rs。

6、提供 Makefile
最后,在工作区的根目录下提供一个是很好的做法Makefile。此文件的目的不是为了节省您的一些按键,而是为了规范构建、测试和维护项目所需的重要且重复的任务,以便任何贡献者都可以参与维护。

#############################################################################################<strong># Dev</strong>
#############################################################################################<strong>.PHONY: update_dependencies</strong>
update_dependencies:
    <strong>...</strong>

.PHONY: audit_dependencies
audit_dependencies:
    <strong>...</strong>

.PHONY: clean
clean:
    <strong>...</strong>

#############################################################################################<strong># Build</strong>
#############################################################################################<strong>.PHONY: build</strong>
build:
    <strong>...</strong>

.PHONY: docker_build
docker_build:
    <strong>...

当一切都被编码后,项目的维护就不再是少数巫师调用没人能理解的奇怪命令行的特权了。