通过包和模块来组织Rust项目

文章目录

Rust是一种现代的系统编程语言,具有高效性、安全性和并发性等特点。它的构建系统和包管理系统通过Cargo工具提供支持,帮助开发者管理项目、工作空间、包和模块。下面详细介绍Rust中的相关概念及其用法。

项目(Project)

在Rust中,项目是一个包含源代码、依赖和配置文件的文件夹。项目通常由一个根目录和多个子目录组成,根目录下通常包含Cargo.toml和Cargo.lock文件。

Cargo.toml是一个配置文件,定义了项目的元数据,如名称、版本、作者等信息,同时指定了项目的依赖、构建配置等内容。

Cargo.lock是一个锁文件,记录了项目依赖的确切版本,确保不同环境中的依赖版本一致。

一个Rust项目可以是一个可执行文件(binary)或库(library),项目通常以Cargo管理,创建项目的命令如下所示:

shell 复制代码
cargo new project_name  

一个简单的Rust的项目结构如下所示:

rust 复制代码
my_project/
├── Cargo.toml
├── Cargo.lock
└── src/
    └── main.rs

工作空间(WorkSpace)

工作空间是一个包含多个包的项目集合,允许你在同一个仓库中管理多个相关的包。工作空间的根目录也包含一个Cargo.toml文件,它通过members字段列出所有包含的包。

工作空间的好处包括:

1.共享一个Cargo.lock文件,确保所有包使用相同版本的依赖。

2.更容易管理和构建多个包。

3.可以将多个包作为一个整体进行构建和测试。

工作空间的结构如下:

rust 复制代码
my_workspace/
├── Cargo.toml  <-- 工作空间的配置文件
├── package1/
│   ├── Cargo.toml
│   └── src/
│       └── lib.rs
└── package2/
    ├── Cargo.toml
    └── src/
        └── lib.rs

my_workspace/Cargo.toml文件内容如下:

ini 复制代码
[workspace]
members = ["package1", "package2"]

包(package)

包是Rust中用于管理项目的基本单位。一个包可以包含一个或多个模块,通常代表一个功能完整的单元或者库。Rust中的包是通过Cargo.toml文件进行管理的。它指定了包的元数据,包括包的名称、版本、依赖关系等。

包是一个独立的可编译单元,它编译后会生成一个可执行文件或者一个库

一个 Package 只能包含一个库(library)类型的包,但是可以包含多个二进制可执行类型的包。

rust 复制代码
//创建执行程序的项目  
cargo new my-project
//创建库的项目 
 cargo new my-lib --lib

一般包的结构如下所示:

典型的项目结构

rust 复制代码
.
├── 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

模块(Module)是Rust中用于组织代码的方式,帮助你将代码分解为多个逻辑单元。模块在Rust中用于命名空间管理,可以通过mod关键字来定义。

主要特点:

1.嵌套模块,Rust允许模块的嵌套,即一个模块中可以定义子模块。

2.私有性和公有性,Rust模块默认是私有的(private),这意味着模块内部的函数、结构体和常量默认不能被外部访问。如果要让它们可以被外部访问,需要显式地声明为公有(public)。

3.文件结构,模块的定义通常与文件系统中的文件结构紧密对应。

例如一个名为foo的模块,通常在src/foo.rs文件中定义或者在src/foo/mod.rs文件中定义。

模块目录结构如下:

rust 复制代码
//main.rs和lib.rs是根模块。
//utils/mod.rs定义了一个名为utils的子模块。
my_project/
├── src/
│   ├── main.rs
│   ├── lib.rs
│   └── utils/
│       └── mod.rs

常见用法如下:

1.创建嵌套模块

rust 复制代码
//创建嵌套模块  
//所有模块均定义在同一个文件中  
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

2.模块的引用

引用方式选择原则: 需要遵循一个原则:当代码被挪动位置时,尽量减少引用路径的修改

rust 复制代码
//绝对路径,从包根开始,路径名以包名或者crate作为开头
//相对路径,从当前模块开始,以self,super或当前模块的标识符作为开头
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}
pub fn eat_at_restaurant() {
    // 绝对路径
    crate::front_of_house::hosting::add_to_waitlist();

    // 相对路径
    front_of_house::hosting::add_to_waitlist();
}

3.模块的可见性

结构体和枚举的可见性

1.将结构体设置为pub,但它的所有字段依然是私有的

2.将枚举设置为pub,它的所有字段也将对外可见

3.结构体成员对于自身和子模块可以视作永远是public的,对于其他模块来说默认是private的

rust 复制代码
//Rust出于安全的考虑,默认情况下所有的类型都是私有化的  
//父模块完全无法访问子模块中的私有项,但是子模块却可以访问父模块、父父..模块的私有项  
//Rust提供了pub关键字,通过它你可以控制模块和模块中指定项的可见性  
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

4.相对路径的引用方式Super

rust 复制代码
fn serve_order() {}

mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        //通过super调用  
        super::serve_order();
    }

    fn cook_order() {}
}

5.相对路径的引用方式self

rust 复制代码
fn serve_order() {
    self::back_of_house::cook_order()
}

mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        crate::serve_order();
    }

    pub fn cook_order() {}
}

6.模块与文件分离

rust 复制代码
//front_of_house前厅分离出来,放入一个单独的文件中 src/front_of_house.rs
pub mod hosting {
    pub fn add_to_waitlist() {}
}

//src/lib.rs文件中引入  
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
} 

使用use导入包

一个包通常包含多个模块,模块之间通过mod和use进行交互,通过use的方式导入别的模块或者函数。

优先使用最细粒度(引入函数、结构体等)的引用方式,如果引起了某种麻烦,再使用引入模块的方式.

rust 复制代码
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

//引入模块
use crate::front_of_house::hosting;
//引入模块中的函数  
use front_of_house::hosting::add_to_waitlist;

//使用父模块的方式来调用 避免同名引用  
use std::fmt;
use std::io;

fn function1() -> fmt::Result {
}

fn function2() -> io::Result<()> {
}

//通过as指定别名 避免同名引用   
use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
}
fn function2() -> IoResult<()> {
}

//引入项再导出
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

使用第三方包

修改Cargo.toml文件,在 [dependencies] 区域添加一行:rand = "0.8.3"

在crates.io或者lib.rs中检索和使用第三方的包

rust 复制代码
use rand::Rng;
fn main() {
    let secret_number = rand::thread_rng().gen_range(1..101);
}

使用{}简化导入

rust 复制代码
use std::collections::{HashMap,BTreeMap,HashSet};
use std::{cmp::Ordering, io};

use std::io;
use std::io::Write;//===>等价于下面  
use std::io::{self, Write};

//使用*引入模块下的所有项
use std::collections::*;

受限的可见性

想要让某一项可以在整个包中都可以被使用有两种办法:

1.在包根中定义一个非pub类型的 X(父模块的项对子模块都是可见的,因此包根中的项对模块树上的所有模块都可见)

2.在子模块中定义一个pub类型的 Y,同时通过 use 将其引入到包根

rust 复制代码
mod a {
    pub mod b {
        pub fn c() {
            println!("{:?}",crate::X);
        }
//方式2子包中定义pub属性
        #[derive(Debug)]
        pub struct Y;
    }
}

//方式1 根包中定义  
#[derive(Debug)]
struct X;
use a::b::Y;
fn d() {
    println!("{:?}",Y);
}

pub(in crate::a) 的方式,我们指定了模块c和常量J的可见范围都只是a模块中,a之外的模块是完全访问不到它们的

rust 复制代码
//限制可见性语法
//pub 意味着可见性无任何限制
//pub(crate)表示在当前包可见  
//pub(self)在当前模块可见  
//pub(super)在父模块可见  
//pub(in <path>)表示在某个路径代表的模块中可见,其中 path 必须是父模块或者祖先模块  
pub mod a {
    pub const I: i32 = 3;

    fn semisecret(x: i32) -> i32 {
        use self::b::c::J;
        x + J
    }

    pub fn bar(z: i32) -> i32 {
        semisecret(I) * z
    }
    pub fn foo(y: i32) -> i32 {
        semisecret(I) + y
    }

    mod b {
        pub(in crate::a) mod c {
            pub(in crate::a) const J: i32 = 4;
        }
    }
}
相关推荐
SomeB1oody6 小时前
【Rust自学】3.3. 数据类型:复合类型
开发语言·后端·rust
Hello.Reader1 天前
从零开始探索 Tauri:迈向轻量级、高性能的跨平台桌面应用开发之路
rust
编码浪子1 天前
构建一个rust生产应用读书笔记四(实战5)
网络·oracle·rust
minos.cpp2 天前
Rust之抽空学习系列(四)—— 编程通用概念(下)
开发语言·学习·rust
CloudWeGo2 天前
ROG:高性能 Go 实现
开发语言·微服务·云原生·架构·golang·rust·开源
YiSLWLL2 天前
使用rust语言创建python模块(pyo3+maturin)
开发语言·python·rust
广东数字化转型3 天前
Less和SCSS,哪个更好用?
开发语言·后端·rust
vvw&3 天前
如何在 Ubuntu 22.04 上安装和使用 Rust 编程语言环境
linux·运维·服务器·ide·ubuntu·rust·github
凌鲨3 天前
Rust学习路线图
学习·rust
无名之逆3 天前
Rust HTTP请求库
服务器·开发语言·后端·网络协议·http·rust·请求