将模块拆分成多个文件

到目前为止,本章所有的例子都在一个文件中定义多个模块。当模块变得更大时,你可能想要将它们的定义移动到单独的文件中,从而使代码更容易阅读。

例如,我们从示例7-11中的代码开始,我们会将模块的代码提取到各自的文件中,而不是将所有模块都定义到 crate 根文件中。在这里,crate 根文件是 src/lib.cairo

首先将 front_of_house 模块提取到其自己的文件中。删除 front_of_house 模块的大括号中的代码,只留下 mod front_of_house; 声明,这样 src/lib.cairo 就包含了代码 如示例7-12所示。注意直到创建示例 7-13 中的 src/front_of_house.cairo 文件之前代码都不能编译。

文件名: src/lib.cairo

mod front_of_house;

use restaurant::front_of_house::hosting;

fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

示例7-12:声明front_of_house 模块,其主体代码将存放在 src/front_of_house.cairo 中。

接下来将之前大括号内的代码放入一个名叫 src/front_of_house.cairo 的新文件中,如示例 7-13所示。因为编译器找到了 crate 根中名叫 front_of_house 的模块声明,它就知道去查看这个文件。

文件名: src/front_of_house.cairo

mod hosting {
    fn add_to_waitlist() {}
}

示例 7-13:在 src/front_of_house.cairo 中定义 front_of_house 模块。

注意你只需在模块树中的某处使用一次 mod 声明就可以加载这个文件。 一旦编译器知道了这个文件是项目的一部分(并且通过 mod 语句的位置知道了代码在模块树中的位置),项目中的其他文件应该使用其所声明的位置的路径来引用那个文件的代码, 这在 引用模块项目的路径部分有讲到。 换句话说,mod 不是 你可能会在其他编程语言中看到的 “include” 操作。

接下来我们同样将 hosting 模块提取到自己的文件中。这个过程会有所不同,因为 hostingfront_of_house 的子模块而不是根模块。我们将 hosting 的文件放在与模块树中它的父级模块同名的目录中,在这里是 src/front_of_house/

为了移动 hosting,修改 src/front_of_house.cairo 使之仅包含 hosting 模块的声明:

文件名: src/front_of_house.cairo

mod hosting;

接着我们创建一个 src/front_of_house 目录和一个包含 hosting 模块定义的 hosting.cairo 文件:

文件名: src/front_of_house/hosting.cairo

fn add_to_waitlist() {}

如果将 hosting.cairo 放在 src 目录,编译器会认为 hosting 模块中的 hosting.cairo 的代码声明于 crate 根,而不是声明为 front_of_house 的子模块。 编译器所遵循的哪些文件对应哪些模块的代码的规则,意味着目录和文件更接近于模块树。

我们将各个模块的代码移动到独立文件了,同时模块树依旧相同。 eat_at_restaurant 中的函数调用也无需修改继续保持有效,即便其定义存在于不同的文件中。 这个技巧让你可以在模块代码增长时,将它们移动到新文件中。

注意,_src/lib.cairo_中的 use restaurant::front_of_house::hosting 语句是没有改变的,在文件作为 crate 的一部分而编译时,use 不会有任何影响。 mod 关键字声明了模块,Cairo 会在与模块同名的文件中查找模块的代码。

总结

Cairo 提供了将包分成多个 crate,将 crate 分成模块,以及通过指定绝对或相对路径从一个模块引用另一个模块中定义的项的方式。 你可以通过使用 use 语句将路径引入作用域,这样在多次使用时可以使用更短的路径。模块定义的代码默认是公有的。

Last change: 2023-09-20, commit: cbb0049