用于引用模块树中项的路径
为了向 Rust 展示在模块树中的什么位置可以找到一个项目,我们在 方式。要调用函数,我们需要 了解其路径。
路径可以采用两种形式:
- 绝对路径是从 crate 根开始的完整路径;对于代码
从外部 crate 中,绝对路径以 crate 名称开头,对于
code 中,它以 Literal
crate
. - 相对路径从当前模块开始,使用
self
,super
或 当前模块中的标识符。
绝对路径和相对路径后跟一个或多个标识符
用双冒号分隔 (::
).
回到示例 7-1,假设我们想调用add_to_waitlist
功能。
这与询问:路径是什么add_to_waitlist
功能?
示例 7-3 包含示例 7-1 以及一些模块和函数
删除。
我们将展示两种调用add_to_waitlist
function 从新函数中获取,eat_at_restaurant
,在 crate 根中定义。这些路径是正确的,但是
还有另一个问题将阻止此示例进行编译
原样。我们稍后会解释原因。
这eat_at_restaurant
function 是我们库 crate 的公共 API 的一部分,因此
我们用pub
关键词。在“使用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
函数使用
绝对路径和相对路径
我们第一次调用add_to_waitlist
函数eat_at_restaurant
,
我们使用 absolute path。这add_to_waitlist
function 定义在相同的
crate 设置为eat_at_restaurant
,这意味着我们可以使用crate
keyword 设置为
开始一个绝对路径。然后,我们包含每个连续的模块,直到我们
前往add_to_waitlist
.您可以想象一个具有相同
structure:我们会指定路径/front_of_house/hosting/add_to_waitlist
自
运行add_to_waitlist
程序;使用crate
name 从
crate 根类似于从 shell 中的文件系统根开始。/
第二次调用add_to_waitlist
在eat_at_restaurant
,我们使用
相对路径。路径始于front_of_house
、模块的名称
在模块树的同一级别定义,与eat_at_restaurant
.这里
文件系统等效的是使用路径front_of_house/hosting/add_to_waitlist
.以模块名称开头的含义
路径是相对的。
选择是使用相对路径还是绝对路径是您将做出的决定
根据您的项目,这取决于您是否更有可能移动
项目定义代码与使用
项目。例如,如果我们将front_of_house
module 和eat_at_restaurant
函数导入到名为customer_experience
,我们会
需要将 absolute path 更新为add_to_waitlist
,但是相对路径
仍然有效。但是,如果我们将eat_at_restaurant
功能
单独导入到一个名为dining
,则是到add_to_waitlist
call 将保持不变,但相对路径需要
被更新。我们通常倾向于指定绝对路径,因为它是
更有可能的是,我们希望独立于
彼此。
让我们尝试编译示例 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
错误消息显示 modulehosting
是私有的。换句话说,我们
具有正确的路径hosting
module 和add_to_waitlist
函数,但 Rust 不允许我们使用它们,因为它无法访问
private 部分。在 Rust 中,所有项目(函数、方法、结构、枚举、
modules 和 constants)是父模块的私有。如果需要帮助,
要将 function 或 struct 等项设为私有,请将其放在 Module 中。
父模块中的项不能使用子模块中的私有项,但 子模块中的项可以使用其祖先模块中的项。这是 因为子模块包装并隐藏了它们的实现细节,但是子 modules 可以看到定义它们的上下文。要继续我们的 比喻,将隐私规则想象成 餐厅:里面发生的事情对餐厅顾客来说是私人的,但是 办公室经理可以在他们经营的餐厅查看和执行所有作。
Rust 选择让模块系统以这种方式运行,以便隐藏内部
implementation details 是默认值。这样,您就知道
内部代码,您可以在不破坏外部代码的情况下进行更改。但是,Rust 确实提供了
您可以选择将子模块代码的内部部分暴露给 outer ancestor
modules 使用pub
关键字将项目设为公共。
使用pub
关键词
让我们回到示例 7-4 中的错误,它告诉我们hosting
module 为
私人。我们希望eat_at_restaurant
函数中,将
访问add_to_waitlist
函数,因此我们将hosting
模块中带有pub
关键字,如示例 7-5 所示。
文件名: 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:声明hosting
module 设置为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
发生了什么事?添加pub
关键字mod hosting
使
模块 public。通过此更改,如果我们可以访问front_of_house
,我们可以
访问hosting
.但是hosting
仍然是私有的;使
module public 不会将其内容公开。这pub
关键字
只允许其祖先模块中的代码引用它,而不访问其内部代码。
因为模块是容器,所以我们只能通过创建
模块 public;我们需要更进一步,选择制作一个或多个
item 中。
示例 7-6 中的错误表示add_to_waitlist
函数是私有的。
隐私规则适用于结构、枚举、函数和方法,以及
模块。
我们还将add_to_waitlist
函数 public 添加pub
关键字,如示例 7-7 所示。
文件名: 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
keyword 设置为mod hosting
和fn add_to_waitlist
让我们从eat_at_restaurant
现在代码可以编译了!要了解为什么添加pub
keyword 让我们使用
这些路径eat_at_restaurant
关于隐私规则,让我们看看
在 absolute 和 relative 路径上。
在绝对路径中,我们从crate
,则 crate 模块的根目录
树。这front_of_house
module 在 crate 根目录中定义。而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
,我们可以访问它的父级
module,所以这个函数调用有效!
在相对路径中,逻辑与绝对路径相同,只是
第一步:路径不是从 crate 根开始,而是从front_of_house
.这front_of_house
module 在同一个 module 中定义
如eat_at_restaurant
,因此从其中的模块开始的相对路径eat_at_restaurant
是定义的工作。那么,因为hosting
和add_to_waitlist
标有pub
,则路径的其余部分将起作用,并且 this
函数调用有效!
如果你打算共享你的库 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 根目录,通过使用super
在
路径。这就像使用..
语法。用super
允许我们引用我们知道在父模块中的项目,
当模块紧密时,这可以使重新排列模块树更容易
与父级相关,但父级可能已移至模块中的其他位置
总有一天树。
考虑示例 7-8 中的代码,它模拟了 chef
修复错误的订单并亲自将其发送给客户。这
功能fix_incorrect_order
在back_of_house
module 调用
功能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
这fix_incorrect_order
函数位于back_of_house
模块,因此我们可以
用super
要转到back_of_house
,在本例中为
是crate
、根。从那里,我们寻找deliver_order
并找到它。
成功!我们认为back_of_house
module 和deliver_order
功能
可能会彼此保持相同的关系并被移动
我们应该一起决定重新组织 crate 的模块树。因此,我们
使用super
因此,如果
代码被移动到不同的模块。
将结构和枚举设为公共
我们还可以使用pub
将结构和枚举指定为 public,但有一个
使用pub
使用结构和枚举。如果我们使用pub
在结构体定义之前,我们将结构体设为公共,但结构体的字段
仍将是私有的。我们可以根据具体情况将每个字段设为公开或不公开
基础。在示例 7-9 中,我们定义了一个 publicback_of_house::Breakfast
结构
使用公共toast
field 中,而是私有的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 和一些 私有字段
因为toast
字段中的back_of_house::Breakfast
struct 是 public,
在eat_at_restaurant
我们可以写入和读取toast
使用 DOT 的字段
表示法。请注意,我们不能使用seasonal_fruit
字段输入eat_at_restaurant
因为seasonal_fruit
是私有的。尝试取消注释
行修改seasonal_fruit
field 值来查看您得到什么错误!
另外,请注意,由于back_of_house::Breakfast
具有私有字段,
struct 需要提供一个公共关联函数,该函数构造一个
实例Breakfast
(我们已将其命名为summer
这里)。如果Breakfast
不
有这样的函数,我们无法创建Breakfast
在eat_at_restaurant
因为我们无法设置私有seasonal_fruit
字段输入eat_at_restaurant
.
相反,如果我们将枚举设为 public,则其所有变体都是 public。我们
只需要pub
在enum
关键字,如示例 7-10 所示。
文件名: 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
enum public,我们可以使用Soup
和Salad
的变体eat_at_restaurant
.
除非它们的变体是公开的,否则枚举不是很有用;那会很烦人
必须用pub
在每种情况下,默认的
for enum variants 是 public。结构体通常很有用,而没有它们的
fields 是公共的,因此 struct 字段遵循一切的一般规则
默认为私有,除非使用pub
.
还有一种情况涉及pub
我们还没有涵盖,那就是
我们的最后一个模块系统功能:use
关键词。我们将涵盖use
本身
首先,然后我们将展示如何组合pub
和use
.
本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准