Rust从0到1-代码组织-路径
下面我们来看一下如何在 Rust 模块树中找到我们需要引用的函数、结构体或枚举等。就像在文件系统使用路径表示一个文件的具体位置一样,我们使用路径(Paths)来表示我们引用的内容的具体位置,譬如,如果我们想要调用一个函数,我们就需要知道它的路径。
路径有两种形式:
绝对路径(absolute path)从 crate root开始,以 crate的名字或者 crate 开头。
相对路径(relative path)从当前模块开始,以
self 、super 或当前模块的名字开头。
绝对路径和相对路径都是由一个或多个由 :: 分割的标识符组成(就像文件系统的 / 路径分隔符)。
继续使用前面餐馆的例子,我们如何调用
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();
}
上面的例子是在同一个 crate 种调用的方式(后面我们会将介绍在不同 crate 中如何调用),我们在
使用相对路径还是绝对路径,主要还是要取决于具体的场景,取决于两部分代码的相对独立性。举例来说,假设我们要将
Rust中模块对代码的组织还包括代码的私有性(privacy boundary):私有代码将不允许外部代码知道、调用和依赖被封装的实现细节。因此,我们可以将,譬如函数或结构体放入模块中来获得私有性。
在Rust中函数、方法、结构体、枚举、模块和常量等默认都是私有的。父模块中不能使用子模块中的私有项,但是子模块可以使用他们父模块中的私有项。这么做是考虑父模块是子模块的上下文,子模块封装并隐藏了自己的实现详情,但是子模块应该可以看到他们所处的上下文(我理解私有性是对外部来说的,不是对内部的)。继续以餐馆作为例子来类私有性规则:餐馆内的后台办公室的情况对餐厅顾客来说是不可知的,但办公室经理可以洞悉其经营的餐厅情况并发布指令。
总之,Rust 选择默认隐藏内部实现细节。这样一来,我们就知道可以放心的去更改内部的哪些部分代码而不会影响外部代码调用。当然,我们还可以通过使用 pub 关键字来创建公开部分,使模块的一部分暴露给外部。因此,上面的例子是无法编译通过的。因为虽然路径是正确的,但是
使用 pub 关键字
我们对前面的例子进行修改让父模块中的
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();
}
不过,编译仍然会报错,为什么呢?我们已经在
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();
}
现在代码可以编译通过了!根据私有性规则,我们从 crate,也就是 crate root 开始看一遍。crate root 中定义了
使用 super
我们还可以使用
fn serve_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::serve_order();
}
fn cook_order() {}
}
公有结构体和枚举
我们还可以使用
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");
}
因为结构体
另外需要注意的是,因为
而枚举与结构体不同,如果我们将枚举设为公有的,则它的所有成员都是公有的:
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;
}
上例中我们创建了名为
另外,还有一种使用