rust 语言--包、Crate 和模块

411 阅读5分钟
  • Packages):Cargo 的一个功能,可以用来构建、测试和分享 crate。
  • Crate :一个由多个模块组成的树形结构,可以作为三方库进行分发,也可以生成可执行文件进行运行,crate 是 Rust 在编译时最小的代码单位。
  • Crates :一个模块的树形结构,它形成了库或二进制项目。
  • 模块Modules)和 use:允许你控制作用域和路径的私有性。
  • 路径path):一个命名例如结构体、函数或模块等项的方式。

包、crate

  1. crate 分为两种形式,二进制和库。
  • 二进制:可以编译为可执行程序,它必须有一个 main函数。
  • :没有main函数,也不能编译为可执行程序,只是提供一些函数之类的东西。可以将其理解为其他编程语言中的 library 类似的概念。
  1. 包(package)是提供一系列功能的一个或者多个 crate。可以理解为工程、软件包。一个包会包含一个 Cargo.toml 文件,描述如何去构建这些 crate。

二进制 Package

创建一个二进制 package,就是之前介绍过的创建 hello_world 项目:

image.png

src/main.rs 是二进制包的根文件,所有的代码执行都从该文件中的 fn main() 函数开始。

库 Package

库的创建需要在后面加上 --lib 参数:

$ cargo new my-lib --lib
     Created library `my-lib` package
$ ls my-lib
Cargo.toml
src
$ ls my-lib/src
lib.rs

库类型的 Package 只能作为三方库被其它项目引用,而不能独立运行,如果一个 Package 包含有 src/lib.rs,该包的根文件是 src/lib.rs

Package 结构

在真实项目中的 Package,会包含多个二进制包,这些包文件被放在 src/bin 目录下,每一个文件都是独立的二进制包,同时也会包含一个库包,该包只能存在一个 src/lib.rs

.
├── Cargo.toml
├── Cargo.lock
├── src
│   ├── main.rs
│   ├── lib.rs
│   └── bin
│       └── main1.rs
│       └── main2.rs
├── tests
│   └── some_integration_tests.rs
├── benches
│   └── simple_bench.rs
└── examples
    └── simple_example.rs

  • 唯一库包:src/lib.rs
  • 默认二进制包:src/main.rs,编译后生成的可执行文件与 Package 同名
  • 其余二进制包:src/bin/main1.rs 和 src/bin/main2.rs,它们会分别生成一个文件同名的二进制可执行文件
  • 集成测试文件:tests 目录下
  • 基准性能测试 benchmark 文件:benches 目录下
  • 项目示例:examples 目录下

模块 Module

Rust中的模块主要用于组织代码,通过把功能相近的代码组织在一起,可以提高代码的可读性和可维护性。另外,模块还可以隐藏内部实现细节,为使用者提供一定的代码封装。

  • crate 根节点:编译crate 时,编译器先在crate 跟文件(src/main.rs 、src/lib.rs)寻找需要编译的代码。
  • 声明模块:需要使用 mod 关键字来声明模块,例如 mod garden 声明的 gargen 模块,编译器会在下面的路径中寻找对应的代码:
    • 内联,mod garden 后面的大括号中
    • src/garden.rs
    • src/garden/mod.rs
  • 私有与公用:一个模块里的代码默认对其父模块私有。为了使一个公用模块内部的成员公用,应当在声明前使用pub
  • use 关键字:使用一个成员的快捷方式。例如 crate::a::b::c的作用域,你可以通过 use crate::a::b::c;创建一个快捷方式,然后你就可以在作用域中只写c来使用该类型。
  1. 模块的语法:在Rust中,我们使用 mod 关键字来定义一个模块,语法如下:
	mod module_name {  
	    // 模块内容  
	}

模块的内容可以包含函数、结构体、枚举等。

2. 使用模块:使用模块时,需要通过 use 关键字引入需要的模块,例如:

	use module_name::function_name;

这样就可以在当前的代码中使用 function_name

3. 模块的层级:Rust中的模块可以有层级,即一个模块可以包含另一个模块。例如:

	mod a {  
	    mod b {  
	        // b模块的内容  
	    }  
	}

在这种情况下,我们可以使用 a::b::function_name 的方式来访问 b 模块中的函数。

  1. 引用模块项目的路径

路径有两种形式:

  • 绝对路径absolute path)是以 crate 根(root)开头的全路径;对于外部 crate 的代码,是以 crate 名开头的绝对路径,对于当前 crate 的代码,则以字面值 crate 开头。
  • 相对路径relative path)从当前模块开始,以 selfsuper 或当前模块的标识符开头。

绝对路径和相对路径都后跟一个或多个由双冒号(::)分割的标识符。

文件名:src/lib.rs

mod a {
    pub mod b {
        pub fn function_c() {}
    }
}

pub fn test() {
    // 绝对路径
    crate::a::b::function_c();

    // 相对路径
    a::b::function_c();
}

虽然 a 模块不是公有的,不过因为它与 test 函数定义于同一模块中(即,a 和 test 是兄弟),我们可以从 test 中引用 a。然后使用 pub 标记的 b 模块以及function_c 函数,以便我们可以访问。

self关键字:当我们需要在一个模块中引用该模块时,可以使用 self 关键字,例如:

	mod a {  
	    fn function_a() {  
	        println!("Function A");  
	    }  

	    fn function_b() {  
	        println!("Function B");  
	        self::function_a(); // 使用self关键字引用本模块的function_a  
	    }  
	}

super关键字:当我们需要在一个子模块中引用其父模块时,可以使用 super 关键字,例如:

	mod parent {  
	    fn function_parent() {  
	        println!("Parent function");  
	    }  
	}  

	mod child {  
	    use super::parent; // 引用父模块  

	    fn function_child() {  
	        println!("Child function");  
	        parent::function_parent(); // 使用super关键字引用父模块的function_parent  
	    }  

	}
  1. Rust标准库中的模块:Rust标准库中包含了很多有用的模块,例如 std::collections 模块中提供了各种常用的数据结构,如 VecStringHashMap 等。我们可以直接使用这些模块中的函数和类型。

  2. 自定义模块的导出:我们可以使用 pub 关键字来导出模块中的函数或类型,例如:

	mod my_module {  
	    pub fn function_a() {  
	        println!("Function A");  
	    }  
	}

这样我们就可以在其他地方使用 my_module::function_a() 来访问这个函数。