如何使用用包,单元包及模块来管理Rust项目

如何使用用包,单元包及模块来管理Rust项目

首页音乐舞蹈fnf游戏mod更新时间:2024-05-07
前言

在之前的文章中,我们都是在Rust Playground上面书写我们的代码例子。在这篇文章我们将简单的介绍rust模块系统,这里包括,package, crate,module和path。通过这一篇文章的学习,我们在编写较为复杂的项目时,合理地对代码进行组织与管理我们的项目代码。

Rust模块系统

Rust提供了一系列的功能来帮助我们管理代码,这其中就包括决定哪些细节是暴露的,哪些细节是私有的,以及不同的作用域内存在哪些名称。这些功能有时被统称为模块系统,它们包括如下几个方面:

Rust 包和单元包(package 和 crate)

包是由一个或多个提供相关功能的单元包集合而成,它所附带的配置文件Cargo.toml描述了如何构建这些单元包的信息。而单元包可以被用于生成二进制程序或库。我们将Rust编译时所使用的入口文件称作这个单元包的根节点,它同时也是单元包的根模块。一个包中只能拥有最多一个库单元包。其次,包可以拥有任意多个二进制单元包。最后,包内必须存在至少一个单元包(库单元包或二进制单元包)。我们用一个例子来说明一下:

➜ cargo new hello Created binary (application) `hello` package

我们使用cargo new命令生成了一个名为hello的二进制单元包,我们使用tree命令查看cargo给我们生成了那些文件,具体操作如下:

➜ tree . └── hello ├── Cargo.toml └── src └── main.rs 3 directories, 2 files

在不加任何参数的情况下cargo将会帮助我们生成一个二进制的crate。cargo会默认将src/main.rs视作一个二进制单元包的根节点而无须指定,这个二进制单元包与包拥有相同的名称。单元包可以将相关的功能分组,并放到同一作用域下,这样便可以使这些功能轻松地在多个项目中共享。

模块系统小实战

我们将使用一个小小的实战来一步步说明Rust中的模块系统。

  1. 使用cargo创建项目,命令如下所示:

➜ cargo new school Created binary (application) `school` package

2. 创建如下目录和文件

➜ school git:(master) ✗ tree src src ├── info.rs ├── main.rs ├── student │ ├── mod.rs │ ├── play.rs │ └── study.rs └── teacher ├── mod.rs └── teaching.rs 3 directories, 7 files

3. 键入如下代码

// info.rs fn print_school_info() { println!("985 and 211"); } // main.rs fn main() { println!("Hello, world!"); }

现在我们需要在main.rs中调用info.rs中的print_school_info函数,我们该怎么做?有人说是直接将info.rs当作模块导入到main.rs当中,这个这想法很好,怎么做呢?由于Rust编译器只能看到crate模块,也就是main.rs,所以我们需要显示的在Rust中构建模块树,需要注意的是文件系统树和模块树之间不存在隐式的转换。具体做法如下所示:

// info.rs fn print_school_info() { println!("985 and 211"); } // main.rs mod info; // 使用mod关键字将info声明为子模块 fn main() { println!("Hello, world!"); info::print_school_info(); // 这里我们使用::语法使用info下的print_school_info函数 }

我们想要把一个文件添加到模块树中,我们需要使用mod关键字来将这个文件声明为一个子模块,就像上面的做法一样。好了,我们现在运行代码

➜ school git:(master) ✗ cargo run Compiling school v0.1.0 (/home/test/Workspace/RustCoder/zhihurust/school) error[E0603]: function `print_school_info` is private --> src/main.rs:5:11 | 5 | info::print_school_info(); | ^^^^^^^^^^^^^^^^^ private function | note: the function `print_school_info` is defined here --> src/info.rs:1:1 | 1 | fn print_school_info() { | ^^^^^^^^^^^^^^^^^^^^^^ For more information about this error, try `rustc --explain E0603`. error: could not compile `school` due to previous error

为什么会出现这种情况?Rust编译器其实已经给出了答案,因为默认情况下模块里面的内容是私有的,我们只需在定义或者声明模块中的函数或常量时使用pub关键字表示函数或常量是公开的就可以了,具体做法如下

// info.rs pub fn print_school_info() { // 在函数前面加入pub关键字 println!("985 and 211"); } // main.rs mod info; fn main() { println!("Hello, world!"); info::print_school_info(); }

现在再来运行修改后的代码,运行结果如下所示:

➜ school git:(master) ✗ cargo run Compiling school v0.1.0 (/home/test/Workspace/RustCoder/zhihurust/school) Finished dev [unoptimized debuginfo] target(s) in 0.09s Running `target/debug/school` Hello, world! 985 and 211

4. 怎么调用目录级别的模块

分别在对应的文件键入如下代码

// src/teacher/teaching.rs pub fn training() { println!("In training"); } // 在src/teacher/目录创建mod.rs文件mod.ts的内容如下 pub mod teaching; // teaching对应文件名,将teaching声明为模块导出 // main.rs mod info; mod teacher; // 文件名称 fn main() { println!("Hello, world!"); info::print_school_info(); teacher::teaching::training(); // 这里的调用意思记作teacher目录下的teaching文件下的training函数 }

teacher目录下的mod.rs文件相当于python中的__init__.py文件,我们运行我们的代码看是否正确,运行结果如下所示:

➜ school git:(master) ✗ cargo run Compiling school v0.1.0 (/home/test/Workspace/RustCoder/zhihurust/school) Finished dev [unoptimized debuginfo] target(s) in 0.10s Running `target/debug/school` Hello, world! 985 and 211 In training

5. 在子模块里调用其他子模块中的函数

分别在对应的文件里键入如下代码

// src/student/mod.rs pub mod study; // src/student/study.rs pub fn do_home_work() { println!("do home work"); } // src/teacher/teaching.rs pub fn training() { println!("In training"); crate::student::study::do_home_work(); // 在teaching这个子模块中调用study.rs子模块的do_home_work函数 } // src/main.rs mod info; mod teacher; mod student; // 需要在主模块中声明student这个子模块,从而才能构建出模块树,不然编译运行不通过 fn main() { println!("Hello, world!"); info::print_school_info(); teacher::teaching::training(); }

注意我们在src/teacher/teaching.rs使用了crate::student::study::do_home_work();这样的语法形式来调用student子模的函数,其中crate的意思是从根crate进行查找也就是main.rs所在的目录,其实就是以绝对路径的方式进行调用。好了我们运行一下代码,看看是否报错,运行结果如下所示:

➜ school git:(master) ✗ cargo run Finished dev [unoptimized debuginfo] target(s) in 0.00s Running `target/debug/school` Hello, world! 985 and 211 In training do home work

6. 使用super关键字调用模块中的函数

如果我们的文件组织包含多级目录,完整的限定名就会变得很长,这时候我们就可以使用super这个关键字来减少这中路径长的问题了。

分别在对应文件下,键入如下代码

// src/student/play.rs pub fn play_football() { println!("play football ..."); } // src/student/mod.rs pub mod study; pub mod play; // src/student/study.rs pub fn do_home_work() { println!("do home work"); println!("below using crate keyword"); crate::student::play::play_football(); // 使用crate关键字调用play子模块中play_football函数 println!("below using super keyword"); super::play::play_football(); // 使用super关键字调用play子模块中play_football函数 }

其实super就是相对路径,即从父模块开始构造相对路径。运行我们的代码看看是否正确吧,运行结果如下所示:

➜ school git:(master) ✗ cargo run Finished dev [unoptimized debuginfo] target(s) in 0.00s Running `target/debug/school` Hello, world! 985 and 211 In training do home work below using crate keyword play football ... below using super keyword play football ...

7. 使用use关键字

无论是使用crate完整的限定名还是使用super相对路径的限定名都很冗长。为了让限定名变得更短,我们可以使用use关键字来给路径绑定一个新名字或者别名。

分别在对应的文件里键入如下代码

// src/student/study.rs pub fn do_home_work() { println!("do home work"); println!("below using crate keyword"); crate::student::play::play_football(); println!("below using super keyword"); super::play::play_football(); } pub fn bye() { println!("goodbye!"); } // src/teacher/teaching.rs use crate::student::study::bye; // use相当于导入操作了 pub fn training() { println!("In training"); crate::student::study::do_home_work(); bye(); // 直接使用bye函数 }

8. 使用外部模块

使用外部模块很简单,只需在Cargo.toml文件里添加对应的模块即可,具体操作如下:

// Cargo.toml文件 [package] name = "school" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] // 在dependencies项下面中添加外部模块 rand = "0.5.5" // rand为外部模块名称, 等号后面为外部模块的版本号 // 保存Cargo.toml文件运行cargo run它就会从crates.io这各站点上下载外部模块了 ➜ school git:(master) ✗ cargo run Updating crates.io index Downloaded rand v0.5.6 Downloaded libc v0.2.140 Downloaded 2 crates (806.4 KB) in 36.19s Compiling libc v0.2.140 Compiling rand_core v0.4.2 Compiling rand_core v0.3.1 Compiling rand v0.5.6 Compiling school v0.1.0 (/home/test/Workspace/RustCoder/zhihurust/school) Finished dev [unoptimized debuginfo] target(s) in 19m 46s Running `target/debug/school` Hello, world! 985 and 211 In training do home work below using crate keyword play football ... below using super keyword play football ... goodbye!

在对应的文件键入如下代码

// src/student/play.rs use rand::Rng; pub fn play_football() { println!("play football ..."); let n = rand::thread_rng().gen_range(2, 11); println!("the n is {}", n); }

运行结果如下所示:

➜ school git:(master) ✗ cargo run Compiling school v0.1.0 (/home/tesst/Workspace/RustCoder/zhihurust/school) Finished dev [unoptimized debuginfo] target(s) in 0.13s Running `target/debug/school` Hello, world! 985 and 211 In training do home work below using crate keyword play football ... the n is 10 below using super keyword play football ... the n is 8 goodbye!

9. 使用通配符,pub use和as

关于使用通配符,pub use和as的用法我就不写代码演示了,我在这简单说一下吧,它们的写法和使用很简单

// 通配符用法 use std::collections::*; // 这跟python里面是类似的,例如from os import * // pub use用法, 比如你在一个子模块里使用了pub use crate::student::play::do_home_work; // 这就相当与你重新把这个模块导出出去了,我们不仅将此条目引入了作用域,而且使该条目可以被 // 外部代码从新的作用域引入自己的作用域 // as 的用法,就是相当于起别名,这一点跟python中的as也是类似的,例如 import os as sysos 小结

在Rust中允许我们将包拆分为不同的单元包,并将单元包拆分为不同的模块,从而使我们能够在其他模块中引用某个特定模块内定义的函数,方法或者常量等。为了引用外部模块,我们需要指定它们的绝对路径或相对路径。我们可以通过use语句将这些路径引入到其作用域中,接着在该作用域中使用较短的路径来多次使用对应的条目。模块中的代码是默认私有的,但我们可以通过添加pub关键字来将定义声明为公有的。

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved