Rust能力养成之(7):管理器与模块

图片

前言

之前的6篇文字,大体上介绍了Rust的基本特征,并带大家领略了一下该语言的类型系统。以此为基础,相信读者能自己写一些基础的程序了,但这还不够,我们需要持续升级,逐渐进入实操实际项目的层次。

在工作中,程序往往是多由文件组成的,而其间存在依赖关系,编译,链接等都不是很简单事情,而这其中所涉及的复杂度是必须管理的。尤其对于大型项目,人工管理已变得不太可能。幸而在Rust世界里,有强大项目管理工具存在。

那么,在这本章这几篇中会介绍Rust如何管理大型项目,以及相关项目管理工具的特色,具体而言,覆盖以下主题:

  • Package管理器

  • 模块

  • Cargo项目管理器和crate作为编译单元

  • 创建和构建项目

  • 运行简易测试

  • Cargo子命令以及第三方包crate的安装

  • 项目实操

本篇先介绍

  • Package管理器

  • 模块

Package管理器

现实中的代码,通常是多文件,多依赖的,因而需要好的管理工具,以保证条理始终清晰。对于常用Python或是R的朋友,应该对各自Package的管理工具所涉及的各种更新,检查有所认识。而对于像C或C++这种相对底层的语言,没有默认的Package管理器,主要用GNU make tool,而该工具语言晦涩,语焉不详,而且经常需要手动添加头文件,还没有内建的下载第三方package的工具,因此不便于长期性的大型项目的管理。

那么,好在Rust不是这样,其拥有切实的Package管理工具,Cargo,可以应对足够复杂的情况,易于创建和维护项目代码,而这就是本篇文字的核心。

模块

作为了解Package管理器的基础,我们先要稍微详细再介绍一下Rust中的模块(Modules),看一下代码是如何组织的。

每个Rust程序都是从root模块开始的:

  • 创建一个库,root模块是lib.rs文件。

  • 创建一个可执行文件,root模块是任何带有主函数的文件,通常是main.rs。

当代码膨胀时,为了在组织项目时提供灵活性,可以分成模块,这里面有多种创建方法。

嵌套模块(Nested modules)

创建模块最简单的方法是在现有模块中使用mod{}块,看下以下代码

// mod_within.rs
mod food {
   
       struct Cake;    struct Smoothie;    struct Pizza;}
//use food::Cake;
fn main() {
   
       let eatable = Cake;}

这里我们构建了一个内部模块,名为food,在大括号内部,声明了三个结构体,Cake, Smoothie, 和Pizza;

在main中,我们创建一个make的实例。代码结果如下:

图片

显然,编译器不知道Cake的定义,此时加上use food::Cake;语句,再编译一下,看下结果,如下所示

图片

原来的错误没有了,但这里得到另外一个错误,提及Cake是私有的,这里引出模块另一个重要特征,默认私有设置。如果需要访问的话,需要两步走:

  • 在模块的声明变量前添加pub关键字

  • 添加food::Cake

 

改成如下​​​​​​​

// mod_within.rs
mod food {
   
       pub struct Cake;  //pub    struct Smoothie;    struct Pizza;}
use food::Cake;
fn main() {
   
       let eatable = Cake;}

代码结果如下,

图片

虽然得到了3个warning,但还是通过编译了。可能有的读者问了,为什么要定义这样一个嵌套的模块,有关其原因和用法,会在详解代码测试(第3章)时候给大家介绍。

文件为模块(File as a moule)

如题,模块可以创建为文件。直接上个实例,这里我建个目录,包含以下结构。

图片

在foo.rs中包含一个结构体Bar,以及其实现impl 内容,代码如下:​​​​​​​

// modules_demo/foo.rs
pub struct Bar;
impl Bar {
   
       pub fn init() {
   
           println!("Bar type initialized");    }}

这时候,我们希望在main.rs中使用这个模块,那么代码如下:​​​​​​​

// modules_demo/main.rsmod foo;
use crate::foo::Bar;
fn main() {
   
       let _bar = Bar::init();}

代码结果如下,

图片

看下main.rs代码,我们先声明了模块foo。然后导入了foo,这里涉及的格式为use crate::foo::Bar, 需要谈一点的是,这里用的前缀是crate,不知道读者是否还有印象,crate在rust指文件包或库文件,我们看看在这里指什么。

一般而言,在rust中,导入模块所需要用到的前缀主要有三种:

  • Absolute imports绝对路径

    • crate,从根节点起,导入与当前文件同级的文件。

  • Relative imports:相对路径

    • self:从当前模块开始

    • Super:从上一级模块开始

目录为模块(Directory as a moule)

以目录作为模块,意味着结构具备了层次性,我们可以在其中创建子模块文件或更多目录层级。设想这样一个情景:我们有一个目录my_program,其中有一个名为foo的模块作为文件foo.rs,包含了一个名为Bar的类型以及foo的功能。随着时间的推移,Bar APIs的数量越来越多,我们希望将其分离为一个子模块。这时就涉及以目录为模块的内容了。

那么我们以代码实现一下,先建立一个目录,在main.rs有一个入口点,再加一个名为foo. rs的目录,而该目录现在包含一个名为bar.rs的子模块,如下所示:

图片

为了让Rust知道bar,我们还需要创建一个名为foo的文件,与在foo目录同级。foo.rs文件将包含在foo目录下创建的任何子模块(这里是bar.rs)的mod声明。

我们看下bar.rs文件的内容:​​​​​​​

//bar.rs
pub struct Bar;
impl Bar {
   
       pub fn hello() {
   
           println!("Hello from Bar !");    }}

这里有一个单元结构体Bar有一个相关的方法hello,我们会在main.rs中使用这个API。接着,看下foo.rs文件:​​​​​​​

//foo.rs
mod bar;
pub use self::bar::Bar;
pub fn do_foo() {
   
       println!("Hi from foo!");}
  • 第3行我们添加了一个模块的声明。

  • 导入模块bar,导入关键词用的self,表明是从模块自身导入,这里将Bar定义为pub,以保证子模块内容可以为父模块使用。这是一种很方便的写法,原因还要到后续篇章中才能揭示。

最后,我们看下main.rs文件:​​​​​​​

// my_program/main.rs
mod foo;
use foo::Bar;
fn main() {
   
       foo::do_foo();    Bar::hello();}

代码结果如下:

图片

可见,程序跑通了。

结语

本篇在介绍了Package管理器之后,着重讨论了rust中模块的内容,而相关实例是为了让读者对如何使用模块能够有些基本的认识,以作为后续学习内容的基础。

下一篇会介绍一下CargoCrates

猜你喜欢

转载自blog.csdn.net/qq_40433634/article/details/113073924
今日推荐