收藏数比赞数还多:)什么情况。快1万字的文章翻译不易,求个赞
不久前,我在推特上发起了 Rust 有什么让人困惑的 话题,热度最高的主题是“模块系统是怎么映射到文件的?”。
我记得刚接触 Rust 时模块让我痛苦挣扎,所以我尝试用一种我认为说得通的方式解释它。
要点
以下所述均使用 Rust 2018 版本。我没有兴趣学习(或教授)老版本的细节,特别是因为老版本让我更加困惑。
如果你有现存的项目,你可以查看 Cargo.toml 文件中的 edtion 查看项目使用的 Rust 版本。 如果没有,那现在就加上 edition = 2018。
如果使用最新的 Rust 且通过 cargo new/ cargo init 来创建新项目,新项目会自动选择 2018 版本。
什么是 crate
一个 crate 通常来说是一个项目。它有一个 Cargo.toml 文件,这个文件用于声明依赖,入口,构建选项等项目元数据。 每个 crate 可以独立地在 https://crates.io/ 上发表。
假设我们要创建一个二进制(可执行)项目:cargo new --bin(或者在已有项目上用 cargo init --bin)会为新 crate 生成一个 Cargo.toml 文件。
项目入口为 src/main.rs
对于二进制项目,src/main.rs 是项目主模块的常用路径。它不一定是精确的路径,可以在 Cargo.toml 添加相应配置 [^1],使编译器在别处查看(甚至可以有多个目标二进制文件和多个目标库)。
默认情况下,我们的可执行项目的 src/main.rs 如下:
fn main(){println!("Hello world!");}
我们可以通过 cargo run 构建和运行这个项目,若只想构建项目,则运行 cargo build
构建一个 crate 的时候,cargo 下载并编译所有所需依赖,默认情况下把临时文件和最终生成文件放入 ./target/ 目录下。 cargo 既是包管理器又是构建系统。
crate 依赖
让我们向刚才创建的 crate 添加 rand 依赖来看看命名空间是怎么工作的。我们需要修改 Cargo.toml,其内容如下:
[package]
name = "modules"
version = "0.1.0"
edition = "2018"
[dependencies]
rand = "0.7.0"
如果我们想学习如何使用 rand crate,有以下几种方式:rand 的 crates.io.page - 上面通常包含了一个类似 README 文件,包含了简要描述和一些代码示例
rand 的 文档(在 crates.io 页面标题或最新版本下有链接)。需要注意的是所有发表在 crates.io 的 crate 会在 https://docs.rs 上生成文件 - 我不确定为什么 rand 也文档部署在它自己的网页,或许它早于 docs.rs?
它的 源码页,如果其他方式(如 crates.io 的链接和自动生成的文档)失败了的化
现在让我们在 src/main.rs 里使用 rand, src/main.rs 如下:
fn main(){letrandom_boolean=rand::random();println!("You {}!",ifrandom_boolean{"win"}else{"lose"});}
请注意:我们不需要使用 use 指令来使用 rand - 它在项目下的文件全局可用,因为它在 Cargo.toml 中被声明为依赖(rust 2018之前的版本则不是这样)
我们完全没必要使用 mod (稍后讲述)
为了明白这篇博客的余下部分,你需要明白 rust 模块仅仅是命名空间 - 他们让你把相关符号组合在一起并保证可见性规则。我们的 crate 有一个主模块(我们现在所在),它的源在 src/main.rs
rand crate 也有一个入口。因为他是一个库,默认情况下其主入口为 src/lib.rs
在我们主模块范围,我们可以在主模块通过依赖名称使用依赖
总之,我们现在只处理两个模块:我们项目主入口还有 rand 的入口。
use 指令
如果我们不喜欢一直这样写 rand::random(),我们可以把 random 注入主模块范围。
userand::random;// 我们可以通过 `rand::random()` 或 `random()` 来使用它fn main(){ifrandom()&&random(){println!("You won twice in a row!");}else{println!("Try again...");}}
我们也可以使用通配符来导入 rand 主模块导出的所有符号。
// 这会导入 random,还有 thead_rng 等userand::*;fn main(){ifrandom(){panic!("Unlucky coin toss");}println!("Hello world");}
模块不需要在分开的文件里
正如刚才所见,模块是一个让你组合相关符号的语言结构。
你不需要把他们放在不同的文件下。
让我们修改下 src/main.rs 来证明这个观点ÿ