通过包和模块来组织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;
        }
    }
}
相关推荐
本地化文档2 小时前
rustdoc-book-l10n
rust·github·gitcode
Tony Bai5 小时前
Rust 看了流泪,AI 看了沉默:扒开 Go 泛型最让你抓狂的“残疾”类型推断
开发语言·人工智能·后端·golang·rust
jump_jump6 小时前
RTK:给 AI 编码助手瘦身的 Rust 代理
性能优化·rust·claude
小杍随笔11 小时前
【Rust Exercism 练习详解:Anagram + Space Age + Sublist(附完整代码与深度解读)】
开发语言·rust·c#
Rust研习社13 小时前
Rust 字符串与切片实战
rust
朝阳58113 小时前
局域网聊天工具
javascript·rust
朝阳58113 小时前
我做了一个局域网传文件的小工具,记录一下
javascript·rust
Rust语言中文社区1 天前
【Rust日报】用 Rust 重写的 Turso 是一个更好的 SQLite 吗?
开发语言·数据库·后端·rust·sqlite
小杍随笔1 天前
【Rust 半小时速成(2024 Edition 更新版)】
开发语言·后端·rust
Source.Liu1 天前
【office2pdf】office2pdf 纯 Rust 实现的 Office 转 PDF 库
rust·pdf·office2pdf