Rust包和crate以及模块
概念
crate 是一个二进制项或者库。crate root 是一个源文件,Rust 编译器以它为起始点,并构成你的 crate 的根模块(我们将在 “DefiningModules to Control Scope and Privacy” 一节深入解读)。包(package) 是提供一系列功能的一个或者多个 crate。一个包会包含有一个 Cargo.toml 文件,阐述如何去构建这些 crate。包中所包含的内容由几条规则来确立。一个包中至多 只能 包含一个库 crate(library crate);包中可以包含任意多个二进制 crate(binary crate);包中至少包含一个 crate,无论是库的还是二进制的。
$ cargo new my-project Created binary (application) `my-project` package $ ls my-project Cargo.toml src $ ls my-project/src main.rs
当我们输入了这条命令,Cargo 会给我们的包创建一个 Cargo.toml 文件。查看 Cargo.toml 的内容,会发现并没有提到 src/main.rs,因为 Cargo 遵循的一个约定:src/main.rs 就是一个与包同名的二进制 crate 的 crate 根。同样的,Cargo 知道如果包目录中包含 src/lib.rs,则包带有与其同名的库 crate,且 src/lib.rs 是 crate 根。crate 根文件将由 Cargo 传递给 rustc 来实际构建库或者二进制项目。在此,我们有了一个只包含 src/main.rs 的包,意味着它只含有一个名为 my-project 的二进制 crate。如果一个包同时含有 src/main.rs 和 src/lib.rs,则它有两个 crate:一个库和一个二进制项,且名字都与包相同。通过将文件放在 src/bin 目录下,一个包可以拥有多个二进制crate:每个 src/bin 下的文件都会被编译成一个独立的二进制 crate。
mod front { // 使用mod来定义模块,里面包含两个模块,其中里面有一些自定义的功能函数 mod hosting { fn add_waitlist() {} fn seat_at_table() {} // 模块还可以保存一些定义的其他项,比如结构体定义模块来控制作用域与私有性 构体、枚举、常量、特性、或者函数 } mod serving { // 使用模块,我们可以将相关的定义分组到一起,并指出他们为什么相关 fn take_order() {} fn server_order() {} fn take_payment() {} } }
在前面我们提到了, src/main.rs 和 src/lib.rs 叫做 crate 根。之所以这样叫它们的原因是,这两个文件的内容都是一个从名为 crate 的模块作为根的 crate 模块结构,称为 模块树(module tree)。这个树展示了一些模块是如何被嵌入到另一个模块的(例如, hosting 嵌套在front_of_house 中)。这个树还展示了一些模块是互为 兄弟(siblings) 的,那如果A模块包含B模块则,A模块称为父模块,B称为子模块
mod front_of_house { mod hosting { fn add_to_waitlist() {} } } pub fn eat_at_restaurant() { // 一个 公共API,所以我们使用 pub 关键字来标记它,是他暴露 // 绝对路径 crate::front_of_house::hosting::add_to_waitlist(); // add_to_waitlist 函数与 eat_at_restaurant 被定义在同一 crate 中,这意味着我们可以 使用 crate 关键字为起始的绝对路径。 // 相对路径 front_of_house::hosting::add_to_waitlist(); }
接下来让我们编译(cargo build) 下上面的代码,会出现如下报错:front_of_house::hosting::add_to_waitlist(); ^^^^^^^ private module; 也就是说路径是没问题的,但是 Rust 不让我们使用,因为它不能访问私有片段,Rust 中默认所有项(函数、方法、结构体、枚举、模块和常量)都是私有的。父模块中的项不能使用子模块中的私有项,但是子模块中的项可以使用他们父模块中的项。这是因为子模块封装并隐藏了他们的实现详情,但是子模块可以看到他们定义的上下文。继续拿餐馆作比喻,把私有性规则想象成餐馆的后台办公室:餐馆内的事务对餐厅顾客来说是不可知的,但办公室经理可以洞悉其经营的餐厅并在其中做任何事情。你还可以通过使用 pub 关键字来创建公共项,使子模块的内部部分暴露给上级模块。
mod front_of_house { pub mod hosting { // mod hosting前添加了pub关键字,使其变成公有的 pub fn add_to_waitlist() {} // 如果我们可以访问 front_of_house ,那我们也可以访问 hosting 。但是 hosting 的 内容 (contents) 仍然是私有的;这表明使模块公有并不使其内容也是公有的。模块上的 pub 关键字只允许其父模块引用它。 } } pub fn eat_at_restaurant() { // 一个 公共API,所以我们使用 pub 关键字来标记它,是他暴露 // 绝对路径 crate::front_of_house::hosting::add_to_waitlist(); // add_to_waitlist 函数与 eat_at_restaurant 被定义在同一 crate 中,这意味着我们可以 使用 crate 关键字为起始的绝对路径。 // 相对路径 front_of_house::hosting::add_to_waitlist(); }