找回密码
 立即注册
首页 业界区 安全 Rust模块系统教程

Rust模块系统教程

捐催制 21 小时前
Rust官方的模块系统教程太啰嗦了,以下内容根据官方教程精简。
Crate

crate 是 Rust 在编译时最小的代码单位。一个crate是一个源文件。
crate 有两种形式:二进制 crate 和库 crate。

  • 二进制 crateBinary crates)必须有一个名为 main 的函数,可以被编译为可执行程序,比如命令行程序或者服务端。
  • 库 crateLibrary crates)并没有 main 函数,它们也不会编译为可执行程序。相反它们定义了可供多个项目复用的功能模块,这与其他编程语言中 “library” 概念一致。
crate root 是一个源文件,Rust 编译器以它为起始点编译代码。
Cargo 遵循一个约定:

  • src/main.rs是一个与包同名的二进制 crate 的 crate root。
  • src/lib.rs是一个与包同名的库 crate 的 crate root。
包(Packages

package)是提供一系列功能的一个或者多个 crate 的捆绑。一个包会包含一个 Cargo.toml 文件。
Cargo创建的项目就是一个包。
一个包(package)可以包含多个二进制 crate 项和一个可选的库 crate。
通过将文件放在 src/bin 目录下,一个包可以拥有多个二进制 crate:每个 src/bin 下的文件都会被编译成一个独立的二进制 crate。
模块(Modules

模块让我们可以将一个 crate 中的代码进行分组,以提高可读性与重用性。因为一个模块中的代码默认是私有的,所以还可以利用模块控制项的私有性(Privacy) 。私有项是不可为外部使用的内在详细实现。我们也可以将模块和它其中的项标记为公开的,这样,外部代码就可以使用并依赖于它们。
声明模块: 在 crate root 文件中,使用 mod 关键字声明一个新模块。例如:
  1. mod garden;
复制代码
内联定义模块:将声明模块后的分号改为大括号,其中提供模块代码。例如:
  1. mod garden {
  2.     // 模块中可定义函数、结构体、枚举、常量、trait等。
  3. }
复制代码
在单独的文件中定义模块:当在crate 根文件中声明了一个模块,例如mod garden;,在src/garden.rs文件中定义模块代码。当在garden.rs中声明了子模块vegetables,则在src/garden/vegetables.rs文件中定义子模块代码。
隐式根模块:src/main.rs 和 src/lib.rs 这两个文件的内容在 crate 模块结构的根组成了一个名为 crate 的模块。
模块树module tree):模块结构是树状结构。
公开模块成员

模块里的成员默认对父模块私有。为了公开模块中的成员,应当在声明前使用pub。
子模块

在模块中声明模块,即子模块。例如在src/garden.rs文件中:
  1. mod vegetables;
复制代码
为了公开子模块,声明时使用 pub mod 替代 mod。
  1. pub mod vegetables;
复制代码
结构体

模块中,结构体的字段也是默认私有的,需要使用pub公开字段:
  1. mod back_of_house {
  2.     pub struct Breakfast {
  3.         pub toast: String,
  4.         seasonal_fruit: String,
  5.     }
  6.     impl Breakfast {
  7.         pub fn summer(toast: &str) -> Breakfast {
  8.             Breakfast {
  9.                 toast: String::from(toast),
  10.                 seasonal_fruit: String::from("peaches"),
  11.             }
  12.         }
  13.     }
  14. }
复制代码
以上代码,因为 back_of_house::Breakfast 具有私有字段,所以这个结构体需要提供一个公共的关联函数来构造 Breakfast 的实例。
枚举

如果将枚举设为公有,则它的所有变体都将变为公有。
  1. mod back_of_house {
  2.     pub enum Appetizer {
  3.         Soup,
  4.         Salad,
  5.     }
  6. }
复制代码
调用模块成员

为了调用模块中的成员,要完整指定成员的路径,多层路径之间用双冒号(::)分割。路径有两种形式:

  • 绝对路径absolute path)是以 crate 根(root)开头的完整路径;对于外部 crate 的代码,是以 crate 名开头的绝对路径,对于当前 crate 的代码,则以字面值 crate 开头。
  • 相对路径relative path)从当前模块开始,以 self、super 或当前模块中的某个标识符开头。
例如:
  1. mod front_of_house {
  2.     pub mod hosting {
  3.         pub fn add_to_waitlist() {}
  4.     }
  5. }
  6. pub fn eat_at_restaurant() {
  7.     // 绝对路径
  8.     crate::front_of_house::hosting::add_to_waitlist();
  9.     // 相对路径
  10.     front_of_house::hosting::add_to_waitlist();
  11. }
复制代码
可以通过在路径的开头使用 super ,从父模块开始构建相对路径,调用父模块成员:
  1. fn deliver_order() {}
  2. mod back_of_house {
  3.     fn fix_incorrect_order() {
  4.         cook_order();
  5.         super::deliver_order();
  6.     }
  7.     fn cook_order() {}
  8. }
复制代码
用use 关键字引入模块成员

使用use 关键字引入模块成员,以便减少调用成员编写过长的路径。
例如引入子模块:
  1. mod front_of_house {
  2.     pub mod hosting {
  3.         pub fn add_to_waitlist() {}
  4.     }
  5. }
  6. use crate::front_of_house::hosting;
  7. pub fn eat_at_restaurant() {
  8.     hosting::add_to_waitlist();
  9. }
复制代码
引入结构体:
  1. use std::collections::HashMap;
  2. fn main() {
  3.     let mut map = HashMap::new();
  4.     map.insert(1, 2);
  5. }
复制代码
通常只引入结构体、枚举和子模块,而不直接引入函数。在调用函数时指定其模块,这样可以清晰地表明函数不是在本地定义的,同时使完整路径的重复度最小化。
use引入的成员只在当前作用域起作用,在子模块中是另一个作用域,不起作用:
  1. mod front_of_house {
  2.     pub mod hosting {
  3.         pub fn add_to_waitlist() {}
  4.     }
  5. }
  6. use crate::front_of_house::hosting;
  7. mod customer {
  8.     pub fn eat_at_restaurant() {
  9.         hosting::add_to_waitlist();  // 此处编译报错
  10.     }
  11. }
复制代码
使用 as 关键字指定别名

不能引入两个同名类型,除非给其中一个类型指定别名。
  1. use std::fmt::Result;
  2. use std::io::Result as IoResult;
  3. fn function1() -> Result {
  4.     // --snip--
  5. }
  6. fn function2() -> IoResult<()> {
  7.     // --snip--
  8. }
复制代码
使用 pub use 重导出名称

使用 use 关键字,将某个名称导入当前作用域后,该名称对此作用域之外还是私有的。若要让作用域之外的代码能够像在当前作用域中一样使用该名称,可以将 pub 与 use 组合使用。这种技术被称为重导出re-exporting),因为在把某个项目导入当前作用域的同时,也将其暴露给其他作用域。
例如在restaurant包中:
  1. mod front_of_house {
  2.     pub mod hosting {
  3.         pub fn add_to_waitlist() {}
  4.     }
  5. }
  6. pub use crate::front_of_house::hosting;
  7. pub fn eat_at_restaurant() {
  8.     hosting::add_to_waitlist();
  9. }
复制代码
在这个修改之前,外部代码需要使用路径 restaurant::front_of_house::hosting::add_to_waitlist() 来调用 add_to_waitlist 函数,并且还需要将 front_of_house 模块标记为 pub。现在这个 pub use 从根模块重导出了 hosting 模块,外部代码现在可以使用路径 restaurant::hosting::add_to_waitlist。
通过 glob 运算符引入成员

如果希望将一个路径下所有公有项引入作用域,可以指定路径后跟 * glob 运算符:
  1. use std::collections::*;
复制代码
使用 glob 运算符时请多加小心!glob 会使得我们难以推导作用域中有什么名称和它们是在何处定义的。
glob 运算符经常用于测试模块 tests 中,这时会将所有内容引入作用域。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册