为了向Rust展示在模块树中找到项目的位置,我们使用的路径与浏览文件系统时使用的路径相同。 如果要调用函数,则需要知道其路径。
路径可以采用两种形式:
绝对路径从板条箱根开始通过使用一个create名称或一个文字create。
相对路径从当前模块开始,并在当前模块中使用self,super或标识符。
绝对路径和相对路径后跟一个或多个标识符,并用双冒号分隔(::)
例如:
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();
}
运行cargo build
会报错:
D:\learn\cargo_learn>cargo build
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
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 {
| ^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0603`.
error: could not compile `cargo_learn`.
To learn more, run the command again with --verbose.
错误消息表明模块托管是私有的。换句话说,我们拥有托管模块和add_to_waitlist函数的正确路径,但Rust无法访问它们,因为它无法访问私有部分。
模块不仅仅对组织代码有用。它们还定义了Rust的隐私边界:封装实现细节的行不允许外部代码知道,调用或依赖。因此,如果要将项目设为函数或结构私有,则将其放在模块中。
Rust中隐私的工作方式是默认情况下所有项目(函数,方法,结构,枚举,模块和常量)都是私有的。父模块中的项目不能使用子模块中的私有项目,但是子模块中的项目可以使用其祖先模块中的项目。原因是子模块包装并隐藏了其实现详细信息,但是子模块可以看到定义它们的上下文。要继续使用餐厅的隐喻,可以将隐私规则视为餐厅的后台办公室:餐厅客户的隐私是私人的,但办公室经理可以查看并在其经营的餐厅中做任何事情。
Rust选择以这种方式使模块系统起作用,以便默认隐藏内部实现细节。这样,您就知道可以更改内部代码的哪些部分而不会破坏外部代码。但是,您可以使用pub关键字将项目公开,从而将子模块代码的内部部分公开给外部祖先模块。
因此我们需要将使用到的paths暴露出来,通过使用关键字pub
:
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();
}
运行:cargo build
D:\learn\cargo_learn>cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.08s
D:\learn\cargo_learn>
通过super
实现相对路径
在rust中mod中的函数通过使用super::
访问与上一级同级别的paths:
fn serve_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::serve_order();
}
fn cook_order() {}
}
fn main() {}
公开结构和枚举
我们还可以使用pub将结构和枚举指定为公共,但还有一些其他细节。 如果我们在结构定义之前使用pub,则将结构公开,但结构的字段仍将是私有的。 我们可以根据情况将每个字段公开或不公开。 在下例中,我们定义了一个公共的back_of_house :: Breakfast结构,其中包含一个公共的toast字段,但一个私有的season_fruit字段。 这样可以在一家餐厅中模拟情况,客户可以在这家餐厅选择餐点附带的面包类型,但是厨师会根据季节和库存来决定餐点中搭配哪种水果。 可用的水果变化很快,因此客户无法选择水果,甚至看不到他们会得到哪种水果。
#![allow(unused_variables)]
fn main() {
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() {
// 通过summer订购黑麦面包早餐
let mut meal = back_of_house::Breakfast::summer("Rye");
// 改变主意,想吃什么面包
meal.toast = String::from("Wheat");
println!("我喜欢 {} 吐司,谢谢", meal.toast);
// 如果我们取消注释,则下一行将不会编译; 我们不允许查看或修改seasonal_fruit
// meal.seasonal_fruit = String::from("blueberries");
}
}
与之不同的是,如果我们将一个枚举公开,则其所有变体都将公开。我们只需要pub在enum关键字之前,如下所示:
#![allow(unused_variables)]
fn main() {
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;
}
}
因为我们公开了Appetizer枚举,所以我们可以在eat_at_restaurant中使用Soup和Salad变体。 枚举不是很有用,除非它们的变体是公开的。 在每种情况下都必须用pub注释所有枚举变量会很烦人,因此枚举变量的默认设置是公开的。 结构通常在不公开其字段的情况下很有用,因此,结构字段遵循通常所有内容都默认为私有的一般规则,除非使用pub注释。