在开始编写代码前,语法概念之类的其实不重要,理解项目的组织架构是首要的。在rust中,模块管理与包的概念与我熟悉的其它语言都有所不同,主要是有Cratemod概念,中文翻译为箱和模块。

Crate

每一个通过cargo创建的项目称之为一个Crate,无论是以库形式(lib.rs)或者是二进制形式(main.rs)。其中唯一的rs文件称之为Crate root,也叫作箱根,也是一个Crate的入口点,子模块的加载之类的,都在对应的箱根中有声明。

系统的标准库也有一个箱根,名为std,我们可以直接引用该Crate而不必在Cargo.toml的依赖中添加,因为安装好rust之后标准库也安装好了,不必从第三方进行下载。

我们自己创建的Crate,假设我们自己别的项目需要加载,则需要在该项目的Cargo.toml[dependencies]节中添加对应的Crate,比如如下的实例:

[package]
name = "helloworld"
version = "0.1.0"
authors = ["sryan"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
sep_restaurant = { path = "../sep_restaurant" }

在上述的实例中,我们引用了一个本地的名为sep_restaurantCrate,而该包的路径是相对于Cargo.toml的。

对于外部的箱根,内部的Crate也就成了创建的项目名了,也就是从Crate变成了helloworld了,当然内部可以直接用Crate作包内的绝对路径引用,这个会在use这段来进行介绍。

mod

rust的模块和go的模块有点儿差异,go是以文件夹作为模块的,并且模块内可以自由引用,rust则可以在单个文件中创建多个模块。

所有模块的根都在箱根中,也就是lib.rs或者是main.rs,所有模块的加载都是在这两个箱根中的,也就是我们在lib.rs中假设定义了一个hello函数:

mod first_level_mod {
    pub fn hello() {
        println!("hello");
    }
}

则该模块是属于箱根的第一层模块:

Crate --- first_level_mod

假设我们继续在first_level_mod中继续新增一个模块:

mod first_level_mod {
    pub fn hello() {
        println!("hello");
    }

    mod second_level_mod {
        pub fn world() {
            println!("world")
        }
    }
}

second_level_mod就是第二层的模块了,对应的父子关系也就变成了:

Crate --- first_level_mod
                  |
                  --- second_level_mod

对于同级的模块,第一层是相互可见的:

mod first_level_mod {
    pub fn hello() {
        println!("hello");
    }

    mod second_level_mod {
        pub fn world() {
            println!("world")
        }
    }
}

pub fn see_mod() {
    first_level_mod::hello();
}

第一层是可见的,我们可以直接访问,但是其中的模块,必须依赖它的可见性标识pub来决定,默认是不可见的。在上述例子中,我们的see_mod函数和first_level_mod都挂在Crate上属于同一级,所以是互相可见的。

上面的例子主要是将所有的代码放到一个文件中,假设我们需要多个文件来拆分项目,我们该怎么操作呢?

我们可以通过在文件中声明一个模块,但是不去实现,类似于我们在lib.rs中下如下的语句:

mod another_mod;

这个时候加载了箱根后,会去加载对应的模块,这个时候有两种加载策略:

  • 读取当前目录下的another_mod.rs文件,其中不必声明模块,所有的代码都在another_mod的模块之中
  • 读取another_mod目录下的mod.rs文件

这样我们就完成了模块与文件分离的操作。我们这里还是首先得明白rust的加载方式,先加载箱根,然后根据箱根内的模块定义与引用来加载不同的模块,所以必定所有的模块都是从箱根这里一级一级的导入的。

假设我们创建了一个another_mod.rs的文件,在其中又定义了一个third_mod.rs,则这里的模块父子关系就是:

Crate --- another_mod --- third_mod

可以看出一层一层的加载模块。在这里我们可以总结出以下模块的加载规律。

当扫描到了模块定义之后(比如为mymod),则根据当前的所属模块,比如当前属于箱根,则直接在箱根同级目录中寻找同名文件mymod.rs或者是同名目录中的mod.rs;若当前属于某一个模块mod_xx,则会寻找mox_xxx/mod.rs。

重点在于除了在箱根定义的模块可以直接在箱根中定义同名文件,其余的都要放入模块对应的全路径中。

说到了模块的多文件,我们假设要引用模块的话,该如何引用呢?

use

最简单的我们假设要引用模块,比如一个标准库,我们可以简单的使用:

let mut hmap: std::collections::HashMap<i32, i32> = std::collections::HashMap::new();

可以看出引用的路径非常的冗长,我们能不能简化一下呢?我们可以通过use关键字来简化引用的路径,比如上述的HashMap我们可以通过use简写成如下的形式:

use std::collections::HashMap;

let mut smap: HashMap<i32, i32> = HashMap::new();

通过use关键字,我们简化了整个HashMap的定义。

假设我们要引入第三方库,则需要更改Cargo.toml了,这个在上述已经提到过了,这里就不再多说了。

共 0 条回复
暂时没有人回复哦,赶紧抢沙发
发表新回复

作者

sryan
today is a good day