Rust中的项目管理
- 前言
- Package,Crate,Module &use ,Path
- 通过代码示例解释 Crate,Module ,use,Path
- 创建一个package:
- 代码组织化
- skin.rs 中的代码
- struct & enum
- 相对路径和绝对路径
- 引用同名模块怎么办?
- use
- pub use
前言
在开发者进行项目立项并逐步开发时,尤为重要的是项目结构和各个模块的管理,在C++中,通常我们结合文件夹以及namespace和头文件的共同作用下将项目结构变得合理,Rust为开发者提供了一整套管理体系如Package,Crates,Module &use ,Path,通过这些特性来共同管理Rust项目,开合自如。
Package,Crate,Module &use ,Path
package:Cargo Feature,可用于build crate,share create & test Crate。
Crate:Rust编译器可编译的最小块,可编译出二进制文件或者库,crate在写法上便是模块树,包含了一系列具有相关性的代码,通过目录和mod关键字组合而成。
Module &use : 在整个工程中起到组织,限制,控制代码隐私的作用
Path: 描述所需具体模块以及方法所在位置的手段,结合use达到C++中的include 效果。
通过代码示例解释 Crate,Module ,use,Path
创建一个package:
cargo new mypackage --lib
可以看到如下的目录结构:
上述便是package的具体表现形式,package允许libraries和execute同时存在,但是在一个package中,只能有一个lib,可以有多个executes,但是不论如何,package一定要包含一个crate,不论是 binary or library。
在省略–lib的状态下, 会在src中直接创建 一个main.rs,加上–lib 创建项目时会在src中创建一个lib.rs ,不论是lib.rs还是main.rs都是crate root,既模块树的树根,抑或是package编译每一个crate的入口root,cargo会将跟文件传递给rustc从而进行编译lib.rs对应与package同名的库,main.rs对应与package同名的二进制可执行文件。
代码组织化
示例工程结构如下:
rust中的模块树如同操作系统中的目录树结构一样,依赖文件夹以及文件夹命名,如图中所示绿色框中的hairs.rs 与文件夹hairs,一般情况下,为了工程代码的整洁,通常不会将所有的代码放在hairs.rs 中,但是需要设计的层级关系,如图中关系,hair->skin->bones,在工程中就可以像图中一样处理,简单来讲 就是子模块放在父模块同级同名文件夹内部,代码文件名xxx.rs在Path 既寻找指定代码依赖时同样重要。 古早的写法:/xxx/xxx/mod.rs现在依然支持但是不再推荐了
。可以这样简单的理解,模块名称仅和文件名称、mod xxx 有关,文件夹只是寻路以及整理文件的作用。
skin.rs 中的代码
pub mod bones;
pub mod muscle {
#[derive(Debug)]
pub struct Muscle {
pub name: String,
pub muscle_group: String,
}
impl Muscle {
pub fn new(name: &str, muscle_group: &str) -> Muscle {
Muscle {
name: name.to_string(),
muscle_group: muscle_group.to_string(),
}
}
pub fn get_muscle_group(&self) -> &str {
&self.muscle_group
}
pub fn get_name(&self) -> &str {
&self.name
}
}
}
pub mod fat {
#[derive(Debug)]
pub struct Fat {
pub percentage: f64,
}
impl Fat {
pub fn new(percentage: f64) -> Self {
Fat { percentage }
}
fn get_percentage(&self) -> f64 {
self.percentage
}
}
}
可以看到,在这里我们将bones模块暴露出去,并在skin包内部定义了Muscle 和 Fat 模块以及结构体和方法。
需要注意的是,模块默认的内部全部都是私有化的,所以如果不加上pub,外部无法访问使用。
struct & enum
struct 内部私有,enum内部pub,这是他们的不同之处。
相对路径和绝对路径
与目录结构一样,我们既可以使用绝对路径也可以使用相对路径use 我们需要的模块,如何选择?根据现实情况,当N个module在同一个父模块中,使用super简单快捷, self一般个人不是很喜欢用,当module来源于父模块之外,用绝对路径。对于引用模块中的函数来讲,直接引入函数的父模块更方便,一般情况下你不会仅使用一个模块中的一个方法,除非你的代码出现了什么设计上的问题,但是如果是使用结构体容器之类,那最好是包含到最后一层。
还记得我们再创建工程时生成的lib.rs吗?来看看其内部代码:
mod Body;
pub mod body;
pub mod cloths;
pub mod hairs;
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
这里的use super::*;
便是使用模块中的所有子模块,为了unit test,关于uint test ,已经在Rust中的测试一章细聊过。
引用同名模块怎么办?
Rust中,你可以在使用时用全路径,亦或使用as 起个别名,具体的,这没有约定俗成的建议,完全取决于开发者的习惯。
代码示例:
use std::io::Result as IoResult;
use
代码示例:
use mypackage::{body, cloths::Cloths, hairs::skin::muscle::Muscle};
fn main() {
let hair_color = Muscle::new("kangkang", "groupOne");
println!("{:?}", hair_color.get_muscle_group());
let cloth = Cloths::new("skirt", "yellow");
println!("{:?}", cloth.get_name());
let mybody = body::Body::new();
mybody.show();
}
use crate::hairs::skin::bones;
use crate::hairs::skin::{fat, muscle};
#[derive(Debug)]
pub struct Body {
my_bones: bones::Bones,
my_fat: fat::Fat,
my_muscle: muscle::Muscle,
}
impl Body {
pub fn new() -> Self {
Body {
my_bones: bones::Bones::new("humanBones", 206),
my_fat: fat::Fat::new(15.0),
my_muscle: muscle::Muscle::new("muscle", "arms"),
}
}
pub fn show(&self) {
println!("{:?}", &self);
}
}
pub use
这里既是开放使用某个模块,使用它相当于开发者在使用处直接“创建”了这个模块,在使用时便只需引用到使用处的模块即可,这多数用在我们自己编写crate时候,由于封装性我们将很多数据结构封装的很深,而使用方又不得不使用,而我们又不想暴露更多细节给到用户,尤其是module层级较深时,对于使用者也是一个难题,pub use便可以很好的解决这样的问题。
“每天怎么过,都是我们的选择”