用于引用模块树中项的路径
为了向 Rust 展示在模块树中的什么位置可以找到一个项目,我们在 方式。要调用函数,我们需要 了解其路径。
路径可以采用两种形式:
- 绝对路径是从 crate 根开始的完整路径;对于代码
从外部 crate 中,绝对路径以 crate 名称开头,对于
代码,它以文本 .
crate
- 相对路径从当前模块开始,使用 、 、 或
当前模块中的标识符。
self
super
绝对路径和相对路径后跟一个或多个标识符
用双冒号 () 分隔。::
回到示例 7-1,假设我们想调用这个函数。
这与询问:函数的路径是什么相同?
示例 7-3 包含示例 7-1 以及一些模块和函数
删除。add_to_waitlist
add_to_waitlist
我们将展示两种从 crate 根中定义的新函数 中调用函数的方法。这些路径是正确的,但是
还有另一个问题将阻止此示例进行编译
原样。我们稍后会解释原因。add_to_waitlist
eat_at_restaurant
该函数是我们库 crate 的公共 API 的一部分,因此
我们用关键字标记它。在“使用 pub
关键字公开路径”部分中,我们将更详细地介绍 .eat_at_restaurant
pub
pub
文件名: src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
示例 7-3 add_to_waitlist
:使用
绝对路径和相对路径
我们第一次在 中调用 函数时,
我们使用 absolute path。该函数在相同的
crate 设置为 ,这意味着我们可以使用关键字
开始一个绝对路径。然后,我们包含每个连续的模块,直到我们
前往 。您可以想象一个具有相同
structure:我们将指定
运行程序;使用 name 从
crate 根类似于从 shell 中的文件系统根开始。add_to_waitlist
eat_at_restaurant
add_to_waitlist
eat_at_restaurant
crate
add_to_waitlist
/front_of_house/hosting/add_to_waitlist
add_to_waitlist
crate
/
第二次调用 时,我们使用
相对路径。路径以 开头,模块的名称
在模块树的同一级别定义。这里
文件系统等效项将使用路径 .以模块名称开头的含义
路径是相对的。add_to_waitlist
eat_at_restaurant
front_of_house
eat_at_restaurant
front_of_house/hosting/add_to_waitlist
选择是使用相对路径还是绝对路径是您将做出的决定
根据您的项目,这取决于您是否更有可能移动
项目定义代码与使用
项目。例如,如果我们将模块和函数移动到名为 的模块中,我们将
需要将绝对路径更新为 ,但相对路径
仍然有效。但是,如果我们将函数
单独放入名为 的模块中,调用的绝对路径将保持不变,但相对路径需要
被更新。我们通常倾向于指定绝对路径,因为它是
更有可能的是,我们希望独立于
彼此。front_of_house
eat_at_restaurant
customer_experience
add_to_waitlist
eat_at_restaurant
dining
add_to_waitlist
让我们尝试编译示例 7-3 并找出为什么它还不能编译!这 我们得到的错误如示例 7-4 所示。
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: module `hosting` is private
--> src/lib.rs:9:28
|
9 | crate::front_of_house::hosting::add_to_waitlist();
| ^^^^^^^ --------------- function `add_to_waitlist` is not publicly re-exported
| |
| private module
|
note: the module `hosting` is defined here
--> src/lib.rs:2:5
|
2 | mod hosting {
| ^^^^^^^^^^^
error[E0603]: module `hosting` is private
--> src/lib.rs:12:21
|
12 | front_of_house::hosting::add_to_waitlist();
| ^^^^^^^ --------------- function `add_to_waitlist` is not publicly re-exported
| |
| private module
|
note: the module `hosting` is defined here
--> src/lib.rs:2:5
|
2 | mod hosting {
| ^^^^^^^^^^^
For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant` (lib) due to 2 previous errors
示例 7-4:在 中构建代码的编译器错误 示例 7-3
错误消息指出该模块是私有的。换句话说,我们
为模块和函数提供了正确的路径,但 Rust 不允许我们使用它们,因为它无法访问
private 部分。在 Rust 中,所有项目(函数、方法、结构、枚举、
modules 和 constants)是父模块的私有。如果需要帮助,
要将 function 或 struct 等项设为私有,请将其放在 Module 中。hosting
hosting
add_to_waitlist
父模块中的项不能使用子模块中的私有项,但 子模块中的项可以使用其祖先模块中的项。这是 因为子模块包装并隐藏了它们的实现细节,但是子 modules 可以看到定义它们的上下文。要继续我们的 比喻,将隐私规则想象成 餐厅:里面发生的事情对餐厅顾客来说是私人的,但是 办公室经理可以在他们经营的餐厅查看和执行所有作。
Rust 选择让模块系统以这种方式运行,以便隐藏内部
implementation details 是默认值。这样,您就知道
内部代码,您可以在不破坏外部代码的情况下进行更改。但是,Rust 确实提供了
您可以选择将子模块代码的内部部分暴露给 outer ancestor
modules 的 git 方法将项目设为公共。pub
使用 pub
关键字公开路径
让我们回到示例 7-4 中的错误,它告诉我们模块是
私人。我们希望父模块中的函数具有
访问子模块中的函数,所以我们用关键字标记模块,如图 7-5 所示。hosting
eat_at_restaurant
add_to_waitlist
hosting
pub
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
示例 7-5:将托管
模块声明为 pub
到
从 eat_at_restaurant
使用它
不幸的是,示例 7-5 中的代码仍然会导致编译器错误,因为 如示例 7-6 所示。
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: function `add_to_waitlist` is private
--> src/lib.rs:9:37
|
9 | crate::front_of_house::hosting::add_to_waitlist();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `add_to_waitlist` is defined here
--> src/lib.rs:3:9
|
3 | fn add_to_waitlist() {}
| ^^^^^^^^^^^^^^^^^^^^
error[E0603]: function `add_to_waitlist` is private
--> src/lib.rs:12:30
|
12 | front_of_house::hosting::add_to_waitlist();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `add_to_waitlist` is defined here
--> src/lib.rs:3:9
|
3 | fn add_to_waitlist() {}
| ^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant` (lib) due to 2 previous errors
示例 7-6:在 中构建代码的编译器错误 示例 7-5
发生了什么事?在 前面添加关键字会使
模块 public。通过此更改,如果我们可以访问 ,我们可以
访问。但 的内容仍然是私有的;使
module public 不会将其内容公开。模块 上的 keyword
只允许其祖先模块中的代码引用它,而不访问其内部代码。
因为模块是容器,所以我们只能通过创建
模块 public;我们需要更进一步,选择制作一个或多个
item 中。pub
mod hosting
front_of_house
hosting
hosting
pub
示例 7-6 中的错误表示该函数是 private。
隐私规则适用于结构、枚举、函数和方法,以及
模块。add_to_waitlist
让我们通过在函数定义之前添加关键字来将函数设为 public,如示例 7-7 所示。add_to_waitlist
pub
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
示例 7-7:将 pub
关键字添加到 mod 托管
和 fn add_to_waitlist
中,我们可以从 eat_at_restaurant
调用函数
现在代码可以编译了!要了解为什么添加关键字允许我们使用
这些路径 在隐私规则方面,让我们看看
在 absolute 和 relative 路径上。pub
eat_at_restaurant
在绝对路径中,我们从 开始,这是我们 crate 模块的根
树。该模块在 crate 根目录中定义。While 不是 public,因为函数是
定义在同一个模块中(即 和 是同级),我们可以引用 FROM 。接下来是标有 的模块。我们可以
访问 的父模块 ,以便我们可以访问 。最后,该函数被标记为 ,我们可以访问其父函数
module,所以这个函数调用有效!crate
front_of_house
front_of_house
eat_at_restaurant
front_of_house
eat_at_restaurant
front_of_house
front_of_house
eat_at_restaurant
hosting
pub
hosting
hosting
add_to_waitlist
pub
在相对路径中,逻辑与绝对路径相同,只是
第一步:路径不是从 crate 根开始,而是从 .该模块在同一个模块中定义
as ,因此从定义 module 开始的相对路径有效。然后,由于 和 被标记为 ,因此路径的其余部分有效,并且 this
函数调用有效!front_of_house
front_of_house
eat_at_restaurant
eat_at_restaurant
hosting
add_to_waitlist
pub
如果你打算共享你的库 crate,以便其他项目可以使用你的代码, 您的公共 API 是您与 crate 用户的合同,它决定了如何 他们可以与您的代码交互。管理有很多注意事项 更改您的公共 API,使人们更容易依赖您的 板条箱。这些考虑超出了本书的范围;如果你是 如果对本主题感兴趣,请参阅 Rust API 指南。
具有二进制文件和库的包的最佳实践
我们提到过,一个包可以同时包含一个 src/main.rs 二进制 crate root 以及 src/lib.rs 库 crate root,并且这两个 crate 都将具有 默认情况下是 Package Name。通常,具有这种 同时包含一个库和一个二进制 crate 将在 binary crate 启动调用库 crate 中代码的可执行文件。 这让其他项目可以从 package 提供,因为库 crate 的代码可以共享。
模块树应在 src/lib.rs 中定义。然后,任何公共项目都可以 通过在 Binary crate 中使用,以包的名称开始 paths 。 二进制 crate 成为库 crate 的用户,就像 external crate 将使用库 crate:它只能使用公共 API。 这有助于您设计一个好的 API;您不仅是作者,还是 客户!
在第 12 章中,我们将演示这个组织 使用包含二进制 crate 的命令行程序进行练习 和一个库板条箱。
使用 super
开始相对路径
我们可以构造从父模块开始的相对路径,而不是
当前模块或 crate 根,通过在
路径。这就像使用语法启动文件系统路径一样。Using 允许我们引用我们知道在父模块中的项目,
当模块紧密时,这可以使重新排列模块树更容易
与父级相关,但父级可能已移至模块中的其他位置
总有一天树。super
..
super
考虑示例 7-8 中的代码,它模拟了 chef
修复错误的订单并亲自将其发送给客户。这
函数调用
函数,方法是指定 的路径 ,以 开头。fix_incorrect_order
back_of_house
deliver_order
deliver_order
super
文件名: src/lib.rs
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}
示例 7-8:使用相对路径调用函数
从 Super
开始
函数在模块中,因此我们可以
用于转到 的父模块 ,在本例中为
是 ,根。从那里,我们寻找并找到它。
成功!我们认为模块和功能
可能会彼此保持相同的关系并被移动
我们应该一起决定重新组织 crate 的模块树。因此,我们
used,因此我们将来更新代码的地方会更少,如果这个
代码被移动到不同的模块。fix_incorrect_order
back_of_house
super
back_of_house
crate
deliver_order
back_of_house
deliver_order
super
将结构和枚举设为公共
我们也可以用于将结构和枚举指定为 public,但有一个
使用 with structs 和 enum 的额外细节。如果我们使用 before a struct 定义,我们会将 struct 设为 public,但 struct 的字段
仍将是私有的。我们可以根据具体情况将每个字段设为公开或不公开
基础。在示例 7-9 中,我们定义了一个 public struct
具有 public 字段,但具有 private 字段。此模型
在餐厅中,顾客可以选择面包类型
随餐提供,但厨师决定哪些水果搭配餐点
关于当季和库存。可用的水果变化很快,因此
客户无法选择水果,甚至无法看到他们将获得哪种水果。pub
pub
pub
back_of_house::Breakfast
toast
seasonal_fruit
文件名: src/lib.rs
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
pub fn eat_at_restaurant() {
// Order a breakfast in the summer with Rye toast
let mut meal = back_of_house::Breakfast::summer("Rye");
// Change our mind about what bread we'd like
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
// The next line won't compile if we uncomment it; we're not allowed
// to see or modify the seasonal fruit that comes with the meal
// meal.seasonal_fruit = String::from("blueberries");
}
示例 7-9:一个带有一些 public fields 和一些 私有字段
因为结构体中的字段是 public 的,所以
in 中,我们可以使用 DOT 对字段进行写入和读取
表示法。请注意,我们不能在 中使用 中的字段,因为 是私有的。尝试取消注释
行修改字段值以查看您得到的错误!toast
back_of_house::Breakfast
eat_at_restaurant
toast
seasonal_fruit
eat_at_restaurant
seasonal_fruit
seasonal_fruit
另外,请注意,由于具有私有字段,因此
struct 需要提供一个公共关联函数,该函数构造一个
实例(我们在此处命名了它)。如果没有
有这样的函数,我们无法创建 in 的实例,因为我们无法在 中设置私有字段的值。back_of_house::Breakfast
Breakfast
summer
Breakfast
Breakfast
eat_at_restaurant
seasonal_fruit
eat_at_restaurant
相反,如果我们将枚举设为 public,则其所有变体都是 public。我们
只需要 before 关键字,如示例 7-10 所示。pub
enum
文件名: src/lib.rs
mod back_of_house {
pub enum Appetizer {
Soup,
Salad,
}
}
pub fn eat_at_restaurant() {
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
}
示例 7-10:将枚举指定为 public 会使其所有 变体 public
因为我们公开了枚举,所以我们可以在 中使用 和 变体。Appetizer
Soup
Salad
eat_at_restaurant
除非它们的变体是公开的,否则枚举不是很有用;那会很烦人
都必须用 注解所有枚举变体,因此默认的
for enum variants 是 public。结构体通常很有用,而没有它们的
fields 是公共的,因此 struct 字段遵循一切的一般规则
默认情况下为私有,除非使用 .pub
pub
还有一种情况我们没有介绍,那就是
我们的最后一个模块系统功能:关键字。我们将单独介绍
首先,然后我们将展示如何组合 和 。pub
use
use
pub
use
本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准