在模块树中引用项目的路径
在模块树中引用项目的路径
为了告诉Rust在模块树中找到一个项目,我们使用路径的方式,就像在文件系统中导航时使用路径一样。为了调用一个函数,我们需要知道它的路径。
路径有两种形式:
- 绝对路径是从板条箱根开始的完整路径;对于外部板条箱的代码,绝对路径以板条箱名称开头,对于当前板条箱的代码,它以字面值
crate
开头。 - 相对路径从当前模块开始,使用
self
、super
或当前模块中的标识符。
绝对路径和相对路径都由一个或多个标识符组成,用双冒号(::
)分隔。
回到清单7-1,假设我们想调用add_to_waitlist
函数。这与询问“add_to_waitlist
函数的路径是什么?”是相同的。清单7-3是在清单7-1中删除了一些模块和函数后的代码。
我们将展示两种方法来从名为eat_at_restaurant
的新函数中调用add_to_waitlist
函数,该函数在板条箱根中定义。这些路径是正确的,但还有另一个问题,这将阻止示例正常编译。我们稍后会解释为什么。
eat_at_restaurant
函数是库板条箱的公共API的一部分,因此我们用pub
关键字标记它。在"使用pub
关键字暴露路径"部分中,我们将详细介绍pub
。
文件名:src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径
front_of_house::hosting::add_to_waitlist();
}
清单7-3:使用绝对和相对路径调用add_to_waitlist
函数
在eat_at_restaurant
中第一次调用add_to_waitlist
函数时,我们使用了绝对路径。add_to_waitlist
函数在与eat_at_restaurant
相同的板条箱中定义,这意味着我们可以使用crate
关键字来启动绝对路径。然后,我们逐个包含每个连续的模块,直到找到add_to_waitlist
。您可以将此想象为具有相同结构的文件系统:我们会指定路径/front_of_house/hosting/add_to_waitlist
来运行add_to_waitlist
程序;使用crate
名称从板条箱根开始,就像在shell中使用/
从文件系统根开始一样。
在eat_at_restaurant
中第二次调用add_to_waitlist
时,我们使用了相对路径。路径以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();
| ^^^^^^^ 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();
| ^^^^^^^ 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` due to 2 previous errors
清单7-4:清单7-3中代码的编译错误
错误消息表示hosting
模块是私有的。换句话说,我们对hosting
模块和add_to_waitlist
函数的路径是正确的,但Rust不允许我们使用它们,因为它没有访问私有部分。在Rust中,所有项目(函数、方法、结构体、枚举、模块和常量)默认为父模块的私有项目。如果要将函数或结构体等项设为私有,则应将其放入模块中。
父模块中的项无法使用子模块内部的私有项,但子模块中的项可以使用其祖先模块中的项。这是因为子模块封装并隐藏其实现细节,但子模块可以看到它们所定义的上下文。继续使用我们的隐喻,将隐私规则视为餐厅的后台:在其中发生的事情对餐厅顾客是私有的,但办公室经理可以在他们经营的餐厅中看到和做任何事情。
Rust选择让模块系统以这种方式工作,以便默认隐藏内部实现细节。这样,您就知道哪些内部代码部分可以更改而不会破坏外部代码。然而,Rust确实给您提供了一种选择,即使用pub
关键字将子模块的内部代码公开给外部祖先模块。
使用pub
关键字暴露路径
让我们回到清单7-4中的错误,该错误告诉我们hosting
模块是私有的。我们希望父模块中的eat_at_restaurant
函数能够访问子模块中的add_to_waitlist
函数,因此我们用pub
关键字标记hosting
模块,如清单7-5所示。
文件名:src/lib.rs
mod front_of_house {
pub mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径
front_of_house::hosting::add_to_waitlist();
}
清单7-5:将hosting
模块声明为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` due to 2 previous errors
清单7-6:清单7-5中代码的编译错误
发生了什么?在mod hosting
之前添加pub
关键字使得该模块变为公共的。通过这个更改,如果我们可以访问front_of_house
,那么我们也可以访问hosting
。但是,hosting
的内容仍然是私有的;使模块公开不会使其内容公开。模块上的pub
关键字只允许其祖先模块引用它,而不允许访问其内部代码。因为模块是容器,只是将模块公开 是做不了什么的;我们需要进一步选择将模块内的一个或多个项目也设为公共。
清单7-6中的错误表明add_to_waitlist
函数是私有的。隐私规则适用于结构体、枚举、函数和方法,以及模块。
让我们通过在fn add_to_waitlist
定义之前添加pub
关键字来使add_to_waitlist
函数也成为公共的,如清单7-7所示。
文件名:src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径
front_of_house::hosting::add_to_waitlist();
}
清单7-7:在mod hosting
和fn add_to_waitlist
之前添加pub
关键字,使我们能够从eat_at_restaurant
中调用该函数
现在代码将编译!为了查看为什么添加pub
关键字使我们能够在add_to_waitlist
中使用这些路径,让我们来看看绝对路径和相对路径。
在绝对路径中,我们从crate
开始,即我们的crate的模块树的根。front_of_house
模块在crate的根中定义。虽然front_of_house
不是公共的,但由于eat_at_restaurant
函数与front_of_house
在同一模块中定义(即eat_at_restaurant
和front_of_house
是兄弟模块),因此我们可以从eat_at_restaurant
引用front_of_house
。接下来是用pub
标记的hosting
模块。我们可以访问hosting
的父模块,因此我们可以访问hosting
。最后,add_to_waitlist
函数标记为pub
,我们可以访问其父模块,因此此函数调用有效!
在相对路径中,逻辑与绝对路径相同,除了第一步:路径不是从crate的根开始,而是从front_of_house
开始。front_of_house
模块在与eat_at_restaurant
定义的同一模块中定义,因此从定义eat_at_restaurant