文章目录
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;
}
}
}