About Hello Rust

Important

最好的学习方法是间隔性重复学习。

一个编程高手是怎样练成的呢? 惟手熟尔。重在刻意练习。 这意为着,就是不断重复练习,实践,再实践,熟练掌握各种技能。因为,只有反复练习,才能真正掌握。

Hello,Rust 是如何产生的呢? 这是我在学习Rust过程中,不断地编写样例代码,不断点滴积累经验,最终形成的。

Rust 是一个非常优秀的系统编程语言,它简洁易读,性能高,更安全,功能强大。然而,它也存在学习曲线陡峭的问题,需要花费大量时间和精力去理解借用检查、所有权机制等新的编程概念,确实不太容易上手。

对于新手来说,Hello, Rust 是一个绝佳的起点。通过这个项目,你不仅能快速入门 Rust 编程,还能通过编程、调试、运行示例代码,迅速掌握 Rust 的核心知识点,熟悉基础语法和基本概念。更棒的是,它还涵盖了高级进阶知识和精选的 Rust Crates 库应用示例。

本书的当前版本假设你使用 Rust 1.86.0(2025-04-03 发布)或更高版本并在所有项目的 Cargo.toml 文件中通过 edition = "2024"将其配置为使用 Rust 2024 edition 惯用法。请查看Getting Started的 “安装” 部分了解如何安装和升级 Rust。

Introduction

Rust 是一种现代、高性能的编程语言,专注于安全性、并发性和速度。由 Mozilla 开发,现由 Rust 基金会维护,广泛应用于系统编程、Web 后端和高性能应用。

Rust 是一种通用的编程语言,强调性能、类型安全和并发性。它强制执行内存安全,确保所有引用都指向有效内存。与传统的垃圾回收机制不同,Rust 通过“借用检查器”在编译时跟踪引用的对象生命周期,从而防止内存安全错误和数据竞争。

Rust 支持多种编程范式。它受到函数式编程思想的影响,包括不可变性、高阶函数、代数数据类型和模式匹配。同时,它通过结构体、枚举、特性和方法支持面向对象编程。

为什么选择 Rust?

  • 安全性:Rust 的“所有权和借用”,通过“借用检查器“在编译时检测内存安全问题,确保了内存安全。
  • 性能:Rust 通过追求零成本抽象(zero-cost abstractions)—— 将高级语言特性编译成底层代码,并且与手写的代码运行速度同样快。Rust 努力确保代码又安全又快速。Rust 的编译器可以生成高效的机器码,接近C的性能。
  • 并发性:Rust 提供了强大的并发模型,支持异步编程,多线程编程,async/await 异步原语,tokio/async-std 异步运行时。
  • 优秀的包管理:Cargo提供Rust 强大的包管理工具, 包版本,依赖,测试一套工具全套方案解决,尤其体验过C/C++ 包管理痛点的人,使用Cargo 感觉太便捷了。
  • 可读性:Rust 的代码风格统一简洁易读。rustfmt 帮你整理代码格式,统一代码风格,便捷阅读。clippy 帮你检查代码问题。

Getting Started

安装 Rust

首先,你需要安装 Rust。你可以从 Rust 官方网站 下载并安装。

Windows 上,你可以从 Rust 官方网站 下 载并安装。

Linux 上,你可以使用包管理器来安装 Rust:

    $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    $ rustup update
    # rustup toolchain install stable
    $ rustup default stable

或者 Ubuntu 下使用以下命令来安装 Rust:

    $ sudo apt-get install rustup
    $ rustup update
    # rustup toolchain install stable
    $ rustup default stable

MacOS 上,你可以使用 Homebrew 来安装 Rust:

    $ brew install rustup
    $ rustup update
    # rustup toolchain install stable
    $ rustup default stable

创建项目

安装完成后,你可以使用以下命令来创建一个新的 Rust项目:

    $ cargo new hello-rust

这将创建一个名为 hello-rust 的项目目录,并在其中创建一个 Cargo.toml 文件,其中包含了项目的依赖信息。 进入项目目录:

    $ cd hello-rust

你应该会看到以下目录结构:

.
├── Cargo.toml
└── src
    └── main.rs

现在你可以编辑 src/main.rs 文件来编写你的 Rust 代码。cargo 会默认生成一个 main.rs 文件,并在其中包含以下代码:

fn main() {
    println!("Hello, world!");
}

main.rs 是一个 Rust 程序的入口点。

或者你可以使用cargo new -lib hello-rust命令,创建的是一个crates库。Cargo 会创建一个lib.rs文件,它包含库的入口点,并且可以被其他 Rust 项目导入和使用。

Cargo.toml 文件内容如下:

[package]
name = "hello-rust"
version = "0.1.0"
edition = "2024"

[dependencies]

Cargo.toml 文件中,

  • [package] 部分定义了项目的名称、版本和 Rust 版本。
  • [dependencies] 部分定义了项目的依赖。在这个例子中,我们没有添加任何依赖,所以我们不需要添加任何依赖。

编译和运行

你可以使用以下命令来编译和运行项目

    $ cargo build
    $ cargo run

这将编译项目并运行 main.rs 文件。运行后,你会看到输出 Hello, world!

一个完整的 Rust 项目结构

Cargo 推荐的目录结构,如下:

  • Cargo.toml 和 Cargo.lock 保存在 package 根目录下
  • 源代码放在 src 目录下
  • Crate子模块源代码放在 crates 目录下
  • 默认的 lib 包根是 src/lib.rs
  • 默认的二进制包根是 src/main.rs
    • 其它二进制包根放在 src/bin/ 目录下
  • 基准测试 benchmark 放在 benches 目录下
  • 示例代码放在 examples 目录下
  • 集成测试代码放在 tests 目录下

测试你的代码

tip

良好的编程习惯,一定要写单元测试。下面先认识下,如何编写一个简单的单元测试,后面会有单独的章节来详细介绍如何编写单元测试。可以先使用Copy的技能,照着样例去写,然后慢慢深入理解。接下来去测试你的第一个 Rust 程序吧。

单元测试的结构

单元测试通常包含以下部分:

  1. 导入模块:使用 use 语句导入需要的模块。
  2. 定义测试函数:使用 #[test] 注解定义测试函数。测试函数应该以 fn 开头,并且返回 ResultOption 类型。
  3. 编写测试代码:在测试函数中编写实际的测试代码。你可以使用断言来验证函数的行为。
  4. 运行测试:使用 cargo test 命令来运行测试。

示例:单元测试


fn main() {
    println!("Hello, world!");
}

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);
    }
}

cargo test 会运行 tests 目录下的所有测试文件。你可以使用以下命令来编译和运行测试,运行上述命令后,你会看到以下输出:

running 1 test
test tests::it_works ... ok

successes:

successes:
    tests::it_works

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

说明你的单元测试通过了。

在这个例子中,add 函数的测试通过了。如果 add 函数返回的值不是 4,测试将会失败。你可以通过修改 add 函数的返回值来验证这一点。

caution

如果你的测试失败了,你可以通过查看 test 目录下的输出文件来找到具体的错误信息。

running 1 test
test tests::it_works ... FAILED

successes:

successes:

failures:

---- tests::it_works stdout ----

thread 'tests::it_works' panicked at src/main.rs:16:9:
assertion `left == right` failed
  left: 4
 right: 5
stack backtrace:
   0: rust_begin_unwind
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:695:5
   1: core::panicking::panic_fmt
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:75:14
   2: core::panicking::assert_failed_inner
   3: core::panicking::assert_failed
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:380:5
   4: hello_rust::tests::it_works
             at ./src/main.rs:16:9
   5: hello_rust::tests::it_works::{{closure}}
             at ./src/main.rs:14:18
   6: core::ops::function::FnOnce::call_once
             at /Users/weirenyan/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   7: core::ops::function::FnOnce::call_once
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.


failures:
    tests::it_works

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

error: test failed, to rerun pass `-p hello-rust --bin hello-rust`

Rust 返回的测试失败结果信息,是很详细的,所以你一定要详细阅读错误信息,看清楚问题所在。最好的方法是通过错误问题,调试代码并解决这些问题,最终可以成功编译和运行项目,整体过程能快速提升代码能力。

note

经过上述简单的旅程,我们已经对 Rust 有了初步的了解。接下来,我们将深入探索 Rust 的核心概念和特性。那么,让我们继续前进吧!开始进入 Rust 的世界旅行吧!

基础入门

变量与表达式

变量

变量绑定是指将一个值赋给一个名称,这样就可以在程序的其他地方使用这个名称来引用该值了。 变量绑定有两部分组成:let关键字和类型注解(type annotation)。

常量

表达式

表达式是计算结果的代码块。它们可以是任何有效的 Rust 代码,包括函数调用、条件语句、循环等。表达式的值会被赋给变量或用于其他目的计算。

基础数据类型

在 Rust 中,每一个值都有一个特定数据类型(data type),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。

Rust 是静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。

标量类型

标量(scalar)类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。你可能在其他语言中见过它们。让我们深入了解它们在 Rust 中是如何工作的。

Rust 支持常见的基本类型:

  • 整型: i8, i16, i32, i64, i128, isize
  • 无符号整型: u8, u16, u32, u64, u128, usize
  • 浮点数: f32, f64
  • 布尔值: bool
  • 字符: char

整型

整型 是一个没有小数部分的数字。我们在Getting Started 部分已经使用过 u64 整数类型。该类型声明表明,它关联的值应该是一个占据 64 比特位的无符号整数(有符号整数类型以 i 开头而不是 u)。表格 1-1 展示了 Rust 内建的整数类型。我们可以使用其中的任一个来声明一个整数值的类型。

Rust 支持多种整数类型: i8, i16, i32, i64, i128, isize 和 u8, u16, u32, u64, u128, usize。 其中 isize 和 usize 是指针大小的整数类型。

表格 1-1 Rust 中的整型:

长度有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
架构相关isizeusize

每一个整型变量都可以是有符号或无符号的,并有一个明确的大小。有符号 和 无符号 代表数字能否为负值,也就是说,这个数字是否有可能是负数(有符号数),或者永远为正而不需要符号(无符号数)。这有点像在纸上书写数字:当需要考虑符号的时候,数字以加号或减号作为前缀;然而,可以安全地假设为正数时,加号前缀通常省略。有符号数以二进制补码形式(two’s complement representation) 存储。

另外,isize 和 usize 类型依赖运行程序的计算机架构:64 位架构上它们是 64 位的,32 位架构上它们是 32 位的。

浮点数

Rust 也有两个原生的浮点数(floating-point numbers)类型,它们是带小数点的数字。Rust 的浮点数类型是 f32 和 f64,分别占 32 位和 64 位。默认类型是 f64,因为在现代 CPU 中,它与 f32 速度几乎一样,不过精度更高。所有的浮点型都是有符号的。

Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取余。整数除法会向零舍入到最接近的整数。下面的代码展示了如何在 let 语句中使用各种数值运算:

#![allow(unused)]
fn main() {
/**
* 数字计算方法。
* 加、减、乘、除、余
*/
pub(crate) fn number_calc() {
    // 加
    let sum = 5 + 11;

    let difference = 45.5 - 4.3;

    // 乘
    let product = 4 * 30;
  
    // 除
    let quotient = 86.7 / 32.2;
   
    // 求余
    let remainder = 63 % 5;

    println!(
        "sum: {}, diff: {}, product: {}, quotient: {}, remainder:{}",
        sum, difference, product, quotient, remainder
    );
}

// 测试运行
 number_calc();

///
/// 单元测试
/// #[cfg(test)]
///
#[cfg(test)]
mod tests {
    // 注意这个惯用法:在 tests 模块中,从外部作用域导入所有名字。
    use super::*;

    #[test]
    fn test_number_calc() {
        let sum = 5 + 10;
        // 加法测试。注意这个断言会导致测试失败。
        assert_eq!(sum, 15);

         number_calc();
    }
}

}

布尔类型

Rust 中的布尔类型有两个可能的值:true 和 false。Rust 中的布尔类型使用 bool 表示。例如:

fn main() {
     let t = true;

    let f: bool = false; // with explicit type annotation
}

使用布尔值的主要场景是条件表达式,例如 if 表达式。

字符类型

Rust 的 char 类型是语言中最原始的字母类型。下面是一些声明 char 值的例子:

fn main() {
    let c = 'z';
    let z: char = 'ℤ'; // with explicit type annotation
    let heart_eyed_cat = '😻';
    let ok = '好';
}

注意,我们用单引号声明 char 字面值,而与之相反的是,使用双引号声明字符串字面值。Rust 的 char 类型的大小为四个字节 (four bytes),并代表了一个 Unicode 标量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。在 Rust 中,带变音符号的字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 char 值。Unicode 标量值包含从 U+0000 到 U+D7FF 和 U+E000 到 U+10FFFF 在内的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 char 并不符合。

Note

什么是数据类型?

在计算机编程语言中,数据类型(Data Type)是用来定义变量或数据的属性,指定其存储方式、取值范围以及可以执行的操作。数据类型为编译器或解释器提供了如何处理数据的指令,包括:

  • 内存分配:决定变量占用多少内存(如 32 位整数占 4 字节)。
  • 操作支持:定义允许的操作(如整数支持加减,字符串支持拼接)。
  • 值范围:限定数据的有效范围(如 i32 的范围是 -2³¹ 到 2³¹-1)。
  • 类型安全:确保操作符合数据类型的规则,防止错误(如避免将字符串当作数字相加)。

数据类型是编程语言的基础,影响程序的性能、内存使用和代码的表达性及可读性。在像 Rust 这样的强类型语言中,数据类型在编译时严格检查,确保类型安全。数据类型的简要历史数据类型的发展与编程语言的演进密切相关:

  • 早期(1950s-1960s):在汇编语言和早期高级语言(如 Fortran、ALGOL)中,数据类型较为简单,主要包括整数、浮点数和字符。例如,Fortran 引入了 INTEGER 和 REAL 类型,用于数值计算。
  • 结构化编程(1970s):随着 Pascal 和 C 的出现,数据类型变得更加丰富,支持结构体(struct)和数组等复合类型,允许更复杂的数据组织。
  • 面向对象编程(1980s-1990s):Smalltalk 和 C++ 引入了类和对象作为数据类型,结合数据和行为。类型系统开始支持继承和多态。
  • 现代语言(2000s-至今):语言如 Java、Python 和 Rust 引入了更高级的类型系统,包括泛型、动态类型、枚举和模式匹配。Rust 等语言强调类型安全和内存安全,引入所有权和生命周期等概念。
  • 类型系统理论:类型理论(如 Hindley-Milner 类型推导)影响了现代语言(如 Haskell、TypeScript),支持更复杂的类型推导和静态检查。

数据类型的演进反映了编程语言对性能、灵活性和安全性的不断追求。数据类型的分类数据类型的分类方式因编程语言而异,但通常可以分为以下几大类:

  1. 按数据结构分类
  • 标量类型(Scalar Types):
    • 表示单一值。
    • 示例:
      • 整数(如 Rust 的 i8, i32, u64):存储整数值。
      • 浮点数(如 f32, f64):存储小数。
      • 布尔值(bool):表示 truefalse
      • 字符(char):存储单个 Unicode 字符(如 Rust 的 char)。
    • 常见于所有语言,用于基本计算。
  • 复合类型(Compound Types):
    • 由多个值组成,封装更复杂的数据结构。
    • 示例:
      • 数组(Array):固定长度的同类型元素集合(如 Rust 的 [i32; 5])。
      • 元组(Tuple):固定长度的异构类型集合(如 Rust 的 (i32, f64, char))。
      • 结构体(Struct):自定义类型,包含命名字段(如 Rust 的 struct)。
      • 枚举(Enum):表示一组可能的值(如 Rust 的 enum)。
      • 字符串:如 Rust 的 String&str,存储文本数据。
      • 集合类型:如向量(Rust 的 Vec<T>)、哈希表(HashMap)等。
    • 复合类型用于组织复杂数据,支持更高层次的抽象。
  1. 按类型检查分类
  • 静态类型(Static Typing):
    • 类型在编译时确定,变量的类型不可更改。
    • 示例:Rust、C、Java。
    • 优点:编译时捕获类型错误,性能更高。
  • 动态类型(Dynamic Typing):
    • 类型在运行时确定,变量类型可动态变化。
    • 示例:Python、JavaScript。
    • 优点:灵活性高,开发速度快。
  • 结构化类型 vs 名称类型:
    • 结构化类型(Structural Typing):类型兼容性基于结构(如 TypeScript 的鸭子类型)。
    • 名称类型(Nominal Typing):类型基于名称(如 Rust、Java)。
  1. 按内存分配分类
  • 基本/原生类型(Primitive Types):
    • 由语言直接支持,存储在栈上,固定大小。
    • 示例:Rust 的 i32, f64, bool, char
  • 引用/堆类型(Reference/Heap Types):
    • 数据存储在堆上,通过引用访问,动态分配内存。
    • 示例:Rust 的 String, Vec<T>, Box<T>
    • 通常涉及指针或引用,需考虑内存管理。
  1. 按功能分类
  • 数值类型:用于计算,如整数、浮点数。
  • 文本类型:如字符串(String, &str)或字符(char)。
  • 逻辑类型:如布尔值(bool)。
  • 集合类型:如数组、列表、字典。
  • 时间类型:如 Rust 标准库的 std::time::Durationchrono::DateTime,用于表示时间和日期。
  • 函数类型:在支持函数式编程的语言中,函数本身也是一种类型(如 Rust 的闭包类型 FnFnMut)。
  • 用户自定义类型:通过结构体、枚举或类定义的类型。
  1. 其他特殊分类
  • 泛型类型(Generic Types):允许类型参数化(如 Rust 的 Vec<T>)。
  • 空类型/可选类型:如 Rust 的 Option<T>,表示可能为空的值。
  • 错误类型:如 Rust 的 Result<T, E>,用于错误处理。
  • 动态类型(Any Types):如 Rust 的 Box<dyn Trait>,支持运行时多态。

复合类型

复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。

元组(tuple)

#![allow(unused)]
fn main() {
/**
 * tupl_sample
 */
pub(crate) fn tupl_sample() {
    println!("datatype tupl_sample .....start");

    let x: (i32, f64, u8) = (500, 6.4, 1);

    //使用dot(.) 获取元组数值,offset 从0开始。
    //获取元组第1个值
    let five_hundred = x.0;

    //获取元组 第2个值
    let six_point_four: f64 = x.1;
    //获取元组 第3个值
    let one: u8 = x.2;

    println!("tupl:({},{},{})", five_hundred, six_point_four, one);

    let s1 = String::from("hello");

    let (s2, len) = calc_length(s1);

    println!("The length of '{}' is {}.", s2, len);

    println!("datatype tupl_sample .....end\n");
}

/**
 * calc length
 */
fn calc_length(s: String) -> (String, usize) {
    let length = s.len(); // len() 返回字符串的长度

    (s, length)
}

}

数组(array)

  • 固定数组 array:

定义:使用方括号 [] 定义一个数组,每个元素之间用逗号分隔。

完整的array样例代码如下:

#![allow(unused)]
fn main() {
///
/// Array Sample
/// 数组
///
pub(crate) fn array_sample() {
    println!("datatype::array_sampe ...... start");

    //定义 12个月 数组
    let months = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    ];

    //获取数组中元素
    println!("Months array first is {:?}", months[0]);
    println!("Months array second  is {:?}", months[1]);

    println!("Months array all is {:?}", months);

    // 编译器自动推导出one的类型
    let one = [1, 2, 3];

    // 显式类型标注
    let two: [u8; 3] = [1, 2, 3];
    let blank1 = [0; 3];
    let blank2: [u8; 3] = [0; 3];

    // arrays是一个二维数组,其中每一个元素都是一个数组,元素类型是[u8; 3]
    let arrays: [[u8; 3]; 4] = [one, two, blank1, blank2];

    // 借用arrays的元素用作循环中
    for a in &arrays {
        print!("{:?}: ", a);
        // 将a变成一个迭代器,用于循环
        // 你也可以直接用for n in a {}来进行循环
        for n in a.iter() {
            print!("\t{} + 10 = {}", n, n + 10);
        }

        let mut sum = 0;
        // 0..a.len,是一个 Rust 的语法糖,其实就等于一个数组,元素是从0,1,2一直增加到到a.len-1
        for i in 0..a.len() {
            sum += a[i];
        }
        println!("\t({:?} = {})", a, sum);
    }

    println!("array_sampe ...... end \n");
}


}
  • 变长数组 Vec:

定义一个变量并使用 vec! 宏来初始化它。vec! 宏会自动推断数组的类型。

或者创建一个空的数组: let mut empty_vec = Vec::new();

完整的Vec样例代码如下:

#![allow(unused)]
fn main() {
///
/// Vec Samle
/// Vector 为动态数组
///
pub(crate) fn vet_sample() {
    println!("vet_sample ......start");

    let v = vec![12, 34, 56, 78];

    let first = v.first();

    println!("ver fist is {:?}", first);

    //fist unwrap option is 12
    println!("ver fist is {}", first.unwrap());

    let mut sum = 0;
    //iter every item
    for n in v {
        println!("vet print item is {}", n);
        sum += n;
    }

    println!("vet all sum is {}", sum);

    //创建一个空的Vec
    let mut empty_vec = Vec::new();
    //push item to vec
    empty_vec.push(123);
    empty_vec.push(456);
    empty_vec.push(789);
    println!("empty vec is {:?}", empty_vec);

    //创建一个包含5个元素的Vec,每个元素都是0
    let zero_vec = vec![0; 5];
    println!("zero vec is {:?}", zero_vec);

    //创建一个包含5个元素的Vec,每个元素都是0
    let mut zero_vec = Vec::with_capacity(5);
    //push item to vec
    zero_vec.push(0);
    zero_vec.push(0);
    zero_vec.push(0);
    zero_vec.push(0);
    zero_vec.push(0);
    println!("zero vec is {:?}", zero_vec);

    println!("vet_sample ......end\n");
}

}

字符串

在 Rust 中,字符串类型是 str,它是不可变的字符串字面量切片,类型为 &str, 是在编译时就固定不可变的。 而 String 是一个字符串类型, 编译时动态分配, 可变长度。

日期时间

  • Rust 标准库时间

Rust 的标准库,std::time 库提供了基础的时间处理功能。

  • SystemTime 是一个表示时间的结构体,它是一个不可变的时间戳。表示一个时间点。 你可以使用 SystemTime 来获取当前时间点,并且可以使用 UNIX_EPOCH 来获取自Unix epoch (1970-01-01 00:00:00 UTC)以来的时间戳(Unixtime)。
  • Instant 是用于高精度单调时间测量(不考虑系统时钟调整),适合性能测试或计时。
  • Duration 是表示一个时间间隔,以秒和纳秒为单位。时间加减:可与 SystemTime 或 Instant 结合使用

以下是使用 SystemTimeInstant 的示例,你可以直接复制到你的 Rust 项目中直接运行,查看输出结果:

#![allow(unused)]
fn main() {
use std::time::{Duration, Instant, SystemTime};
use std::thread::sleep;

/// Sample function to demonstrate time operations.
pub fn time_sample() {
    //Instant
    let now: Instant = Instant::now();
    println!("Instant: {:?}!", now); // 输出当前时间戳

    let elapsed = now.elapsed();
    println!("Elapsed: {:.2?}", elapsed); // 输出经过的时间

    //Duration
    let timeout = Duration::from_secs(1);
    let start = Instant::now();

    sleep(Duration::from_millis(500));

    if start.elapsed() > timeout {
        println!("Timeout!");
    } else {
        println!("Operation completed within timeout.");
    }

    //SystemTime 获取当前时间戳
    let now = SystemTime::now();

    //fetch unixtime, return current time in seconds since Unix epoch.it since 1970-01-01 00:00:00 UTC was seconds ago
    let since_the_epoch = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();

    //unixtime
    println!("Seconds since the epoch: {}s", since_the_epoch.as_secs());

    //当前线程休眠1000ms
    sleep(Duration::from_millis(1000));

    //返回now与当前时间的差值,单位为秒
    let elapsed = now.elapsed().unwrap();
    println!("Seconds since the elapsed: {}s", elapsed.as_secs()); //打印输出 1s
}

///to call run the time_sample function
time_sample();

// to unit test the time_sample function
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_time_sample() {
        time_sample();
    }
}

}

tip

什么是Unix epoch?

Unix epoch 是一个固定的时间点,即 1970年1月1日 00:00:00 UTC。

什么是Unixtime?

Unix time 是 Unix epoch 的时间戳,(也称为 POSIX time 或 epoch time),它是一个自从 Unix epoch 开始经过的秒数,它不考虑闰秒,以简化和标准化时间计算。通常表示为一个整数。简单来说,Unix epoch 是基准点,Unix time 是从这个基准点开始的秒数计数。Unixtime 是一个非常常用的时间戳格式,在许多编程语言和系统中都广泛使用。

Unix Time 的作用和优点:

  • 简化时间存储和计算: Unix time 是一个简单的整数,非常适合在计算机内部存储和进行时间比较、计算等操作。这比处理复杂的日期、月份、年份、时区和闰年规则要简单得多。
  • 跨平台兼容性: 几乎所有主流的操作系统、编程语言和数据库都支持 Unix time,使其成为在不同系统之间传递时间信息时的通用标准。
  • 精确性和一致性: Unix time 避免了时区、夏令时等问题,确保了时间表示的精确性和一致性。它通常以 32 位或 64 位整数存储,能够表示非常长的时间范围。
  • Chrono 库日期时间

Rust 社区提供了 chrono 库来处理日期和时间。 你可以使用 chrono::prelude::NaiveDate 来表示日期和 chrono::prelude::NaiveDateTime 来表示日期时间,并且可以使用 chrono::Duration 来表示时间差。

  • 依赖配置

添加 chrono 库到你的项目中,你可以使用以下命令来安装:


cargo add chrono

或者修改 Cargo.toml 文件中的依赖项:

[dependencies]
chrono = { version = "0.4.39", features = ["serde"] }

  • 示例代码

如何获取当前日期和时间呢? 你可以使用 chrono::Local.now() 来获取当前的本地日期和时间。 你也可以使用 chrono::Utc.now() 来获取当前的 UTC 日期和时间。

获取本地当前日期和时间:


use chrono::prelude::*;
use chrono::{DateTime, Local};
fn main() {
    let now = Local.now();
    println!("Current date and time: {}", now);
}

获取 UTC当前日期和时间:


use chrono::{DateTime, Utc};
fn main() {
    let now = Utc.now();
    println!("Current date and time: {}", now);
}

一个更完成样例代码,展示了如何使用 chrono 库来获取当前日期和时间,并将其格式化为字符串:


fn date_sample() {
    // 使用 from_ymd_opt 创建 NaiveDate
    let date = NaiveDate::from_ymd_opt(2024, 10, 26).unwrap();
    println!("Date: {}", date);

    // 使用 from_hms_opt 创建 NaiveTime
    let time = NaiveTime::from_hms_opt(12, 30, 0).unwrap();
    println!("Time: {}", time);

    // 使用 new 创建 NaiveDateTime
    let datetime = NaiveDateTime::new(date, time);
    println!("DateTime: {}", datetime);

    // 使用 with_ymd_and_hms 创建 DateTime<Utc>
    let utc_datetime = Utc.with_ymd_and_hms(2024, 10, 26, 12, 30, 0).unwrap();
    println!("UTC DateTime: {}", utc_datetime);

    // 使用 with_ymd_and_hms 创建 DateTime<Local>
    let local_datetime = Local.with_ymd_and_hms(2024, 10, 26, 12, 30, 0).unwrap();
    println!("Local DateTime: {}", local_datetime);

    // 获取当前 UTC 时间
    let now_utc = Utc::now();
    println!("Now (UTC): {}", now_utc);

    // 获取当前本地时间
    let now_local = Local::now();
    println!("Now (Local): {}", now_local);

    //日期格式化
    let now = Utc::now();

    // 常用格式
    println!("ISO 8601 / RFC 3339: {}", now.to_rfc3339()); // 推荐的格式
    println!(
        "Year-Month-Day Hour:Minute:Second: {}",
        now.format("%Y-%m-%d %H:%M:%S")
    );
    println!(
        "Day/Month/Year Hour:Minute:Second: {}",
        now.format("%d/%m/%Y %H:%M:%S")
    );
    println!("Month Day, Year: {}", now.format("%B %d, %Y"));
    println!("Weekday, Day Month Year: {}", now.format("%A, %d %B %Y"));

    // 自定义格式
    println!("Custom format: {}", now.format("%a %b %e %T %Y"));

    // 时间戳 (Unix timestamp)
    println!("Timestamp (seconds): {}", now.timestamp());
    println!("Timestamp (milliseconds): {}", now.timestamp_millis());

    //日期解析
    let datetime_str = "2024-10-26 12:30:00";
    let datetime = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S").unwrap();
    println!("Parsed DateTime: {}", datetime);

    let date_str = "2024-10-26";
    let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d").unwrap();
    println!("Parsed Date: {}", date);

    let rfc3339_str = "2024-10-26T12:30:00Z";
    let rfc3339_datetime = DateTime::parse_from_rfc3339(rfc3339_str).unwrap();
    println!("Parsed RFC3339 DateTime: {}", rfc3339_datetime);

    //错误处理
    let invalid_date_str = "2024-13-26";
    let invalid_date = NaiveDate::parse_from_str(invalid_date_str, "%Y-%m-%d");
    match invalid_date {
        Ok(_) => println!("Parsed Date: {:?}", invalid_date),
        Err(e) => println!("Error parsing date: {}", e),
    }
}

了解所有权

什么是所有权?

所有权系统的基本概念

所有权遇到问题

1. 如何解决函数内部定义变量如何返回出来的问题?

这是 Rust 所有权系统的核心问题。你不能返回一个指向函数内部栈上定义的局部变量的引用。 函数执行完毕后,栈上的局部变量会被清理掉,返回的引用将指向无效内存(悬垂指针

// 这是错误的示例!悬垂指针!
fn create_value_and_return_ref<'a>() -> &'a i32 {
    let value = 42; // value 在函数栈上
    // &value // 错误!不能返回指向栈上局部变量的引用,因为它在函数返回后就无效了
}

// 这是正确的,但需要一个外部的生命周期 'a
fn get_ref_from_external_source<'a>(data: &'a i32) -> &'a i32 {
    data // data 是从外部借用进来的,它的生命周期 >= 函数返回的引用生命周期
}

解决方法:

要将函数内部创建的数据“返回”出来,你必须转移该数据的所有权。Rust 的移动语义(Move Semantics)使得这变得简单且安全:

  • 直接返回数据 (按值返回): 函数返回类型是 T,你直接返回函数内部创建的变量。数据的所有权从函数内部转移到调用者。
#![allow(unused)]
fn main() {
fn create_value_and_return_owned() -> i32 {
    let value = 42; // value 在函数栈上
    value // value 的所有权被移出函数
} // value 在这里不会被 drop,因为它已经被移出

fn create_string_and_return_owned() -> String {
    let text = String::from("hello"); // text 在函数栈上,但其数据在堆上
    text // text 的所有权被移出函数。堆上的数据不会被清理。
} // text 在这里不会被 drop

fn create_box_and_return_owned() -> Box<i32> {
    let boxed_value = Box::new(100); // boxed_value 在函数栈上,它指向堆上的数据
    boxed_value // boxed_value 的所有权被移出函数。堆上的数据不会被清理。
} // boxed_value 在这里不会被 drop

}
  • 返回智能指针: 如果你需要共享数据,可以将内部创建的数据包装在 Rc 或 Arc 等智能指针中,并返回智能指针的副本。数据的实际所有权由智能指针管理,而你返回的是智能指针的共享引用或智能指针本身(所有权转移)。
#![allow(unused)]
fn main() {
use std::sync::Arc;
use std::cell::RefCell;

fn create_shared_data() -> Arc<RefCell<i32>> {
    let data = RefCell::new(0); // data 在函数栈上,它包装了堆上的数据
    Arc::new(data) // Arc::new 会将 RefCell 移动到堆上,并返回 Arc 的所有权
} // data 在这里不会被 drop,因为它内部的 RefCell 已经被移到堆上并被 Arc 拥有

fn ownership_shared_sample() {
    let shared = create_shared_data(); // shared 现在拥有 Arc 的所有权
    println!("{}", shared.borrow());
}

///
/// 单元测试
/// #[cfg(test)]
///
#[cfg(test)]
mod tests {
    // 注意这个惯用法:在 tests 模块中,从外部作用域导入所有名字。
    use super::*;

    #[test]
    fn test_ownership_shared_sample() {
        ownership_shared_sample();
        println!("print test in mdbook")
    }
}

}

结构体

结构体是 Rust 中的一种数据结构,它允许你将多个值组合在一起,并且可以定义方法来操作这些值。你可以使用结构体来定义数据类型,这些数据类型可以包含其他值。

结构体样例代码如下:

#![allow(unused)]
fn main() {
/**
 * 结构体 sample
 */
pub(crate) fn struct_sample() {
    println!("datatype sample struct_sample .....start");

    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    println!("Struct update filed value by other struct result.");

    let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    };

    //可以尝试注释以下语句,会报错误,因为user1 所有权已经被借用了,
    //^ print!("user is {:?}", user1);

    //所以user2 拥有所有权 ,可以正常打印
    println!(
        "根据已有的结构体实例,创建新的结构体实例.user2: {:?}",
        user2
    );

    let user3 = build_user(
        String::from("another@example.com"),
        String::from("someusername456"),
    );

    let user4: User = User {
        active: user3.active,
        username: user3.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };

    println!("user3 user.emal:{}", user3.email);

    //以下语句报:
    //borrow of moved value: `user3.username`
    //move occurs because `user3.username` has type `String`, which does not implement the `Copy`
    //可以尝试删除注释
    //^ println!("user3 user.username:{}",user3.username);

    println!("user build result user4 is {:?}", user4);

    println!("datatype sample struct_sample .....end\n");
}

///
///  用户信息 结构体
///
#[derive(Debug)]
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}



}

结构体字段

结构体方法

枚举

模块

模块命名限制:

  • 以字母开头,可以包含数字和下划线。
  • 不可使用特殊字符。

命名约定

自定义模块不能与标准库中的模块冲突。例如: core, std

若模块命名为core,则会出现以下错误。

#![allow(unused)]
fn main() {
error[E0433]: failed to resolve: could not find marker in core
 --> crates/domain/src/repository/monitor_repository.rs:8:1
  |
8 | #[async_trait]
  | ^^^^^^^^^^^^^^ could not find marker in core

}

特征

特征,Rust 定义一组行为方法,类似Java 语言中的接口。

#![allow(unused)]

fn main() {
/// more Trait inherit sample
// Define Trait A
trait A {
    fn method_a(&self);
}

// Define Trait B that inherits from Trait A
trait B: A {
    fn method_b(&self);
}

// Our struct
struct MyStruct;

// Implement Trait B for MyStruct
// This requires MyStruct to also implement Trait A
impl B for MyStruct {
    fn method_b(&self) {
        println!("MyStruct implements method_b");
    }
}

// ERROR! Rust tells us: "the trait `A` is not implemented for `MyStruct`"
// Even though we only explicitly wrote `impl B for MyStruct`,
// because `B: A`, MyStruct *must* also satisfy `A`.
// We need to add the `impl A for MyStruct` block:
// when `A` is empty method ,must to implement "the trait `a`"
impl A for MyStruct {
    fn method_a(&self) {
        println!("MyStruct implements method_a");
    }
}

/// more trait inherit sample
fn multi_inherit_sample() {
    let s = MyStruct;
    s.method_a(); // Can call method from A
    s.method_b(); // Can call method from B

    // We can also treat it as an A trait object because B implies A
    let a_trait_obj: &dyn A = &s;
    a_trait_obj.method_a();

    let b_trait_obj: &dyn B = &s;
    b_trait_obj.method_a(); // Can call method_a through B trait object
    b_trait_obj.method_b();
}

///
/// 单元测试
/// #[cfg(test)]
///
#[cfg(test)]
mod tests {
    // 注意这个惯用法:在 tests 模块中,从外部作用域导入所有名字。
    use super::*;
    #[test]
    fn test_multi_inherit_sample() {
        multi_inherit_sample();
    }
}

}

高级进阶

测试

Mock 测试

Rust 中有类似Java Mockito 的测试Mock框架,mockall 框架。

下面是一个完成Mock 测试样例代码:

#![allow(unused)]

fn main() {
use mockall::automock;
use std::sync::Arc;

/// trait mock sample
#[automock]
trait HmsMonitorService {
    fn monitor(&self) -> bool;
}

#[derive(Clone)]
pub struct MonitorMessageConsumerListener {
    monitor_service: Arc<dyn HmsMonitorService>,
}

/// async trait sample
#[automock]
#[async_trait::async_trait]
trait HmsMonitorAsyncService {
    async fn monitor(&self) -> bool;
}

#[derive(Clone)]
pub struct MonitorMessageController {
    monitor_service: Arc<dyn HmsMonitorAsyncService>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_monitor() {
        let mut mock = MockHmsMonitorService::new();
        mock.expect_monitor().returning(|| true);
        let listener = MonitorMessageConsumerListener {
            monitor_service: Arc::new(mock),
        };
        assert!(listener.monitor_service.monitor());
    }

    #[tokio::test]
    async fn test_monitor_async() {
        let mut mock = MockHmsMonitorAsyncService::new();
        mock.expect_monitor().returning(|| true);
        let listener = MonitorMessageController {
            monitor_service: Arc::new(mock),
        };
        assert!(listener.monitor_service.monitor().await);
    }
}


}

Rspec 测试

文件目录操作

获取 HOME 目录

使用std::env 模块来获取 HOME 目录。也可以使用dotenvy.env 文件中加载环境变量。

安装:

cargo add home
cargo add dotenvy

或者配置Cargo.toml

dotenvy = "0.15.7"
home = "0.5.11"

以下示例代码,主要实现通过dotenv 加载环境变量,获取当前环境变量全部列表配置,并打印出来。

  • 获取 HOME 环境变量。
  • 获取 CARGO_MANIFEST_DIR 环境变量。
  • 获取 Cargo.toml 所在的目录。

样例代码:

#![allow(unused)]
fn main() {
/// # dotenv_sample
/// use dotenvy crate to load environment variables from .env file.
/// Fails if .env file not found, not readable or invalid.
///    dotenvy::dotenv()?;
///
use dotenvy;
use home;
use std::env::{self, home_dir};
use std::error::Error;

/// dotenv_sample
/// use dotenvy crate to load environment variables from .env file.
fn dotenv_sample() -> Result<(), Box<dyn Error>> {
    // Load environment variables from .env file.
    // Fails if .env file not found, not readable or invalid.
    dotenvy::dotenv()?;

    // Iterate over all environment variables
    for (key, value) in env::vars() {
        println!("{key}: {value}");
    }

    // 获取 HOME 环境变量
    let home = dotenvy::var("HOME")?;
    println!("HOME: {}", home);

    // 获取 CARGO_MANIFEST_DIR 环境变量
    let cargo_home = env::var("CARGO_MANIFEST_DIR")?;
    println!("CARGO_MANIFEST_DIR: {}", cargo_home);

    // 获取 Cargo.toml 所在的目录
    let manifest_dir = env!("CARGO_MANIFEST_DIR");
    println!("Cargo manifest directory (Project root): {}", manifest_dir);

    // 构建相对于 Cargo.toml 的路径
    let data_path = format!("{}/data/data.txt", manifest_dir);
    println!("Data file path: {}", data_path);

    // 使用 PathBuf 构建路径(更推荐)
    use std::path::PathBuf;
    let data_path_buf = PathBuf::from(manifest_dir).join("data").join("data.txt");
    println!("Data file path (PathBuf): {}", data_path_buf.display());

    Ok(())
}

fn current_dir_sample() -> Result<(), Box<dyn std::error::Error>> {
    // 获取当前工作目录
    let current_dir = env::current_dir()?;
    println!("Current directory: {}", current_dir.display());

    // 获取可执行文件的完整路径
    let exe_path = env::current_exe()?;

    // 从路径中提取目录部分
    let exe_dir = exe_path
        .parent()
        .ok_or("Could not get executable directory")?;

    println!("Executable directory: {}", exe_dir.display());

    // 获取 HOME 目录
    env::home_dir().map(|home| println!("Home directory: {}", home.display()));

    // 获取 HOME 目录
    home::home_dir().map(|home| println!("Home directory: {}", home.display()));

    // 获取 HOME 目录
    // 优先使用 home crate
    home::cargo_home().map(|home| println!("Cargo home directory: {}", home.display()));

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_dotenv_sample() {
        dotenv_sample().unwrap();
    }

    #[test]
    fn test_current_dir_sample() {
        current_dir_sample().unwrap();
    }
}


}

临时文件

在 Rust 中,你可以使用 std::fs 模块来创建和管理临时文件。

安装:

cargo add tempfile

或者配置Cargo.toml

tempfile = "3.10.0"

样例代码:

#![allow(unused)]
fn main() {
use std::env;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};

use tempfile::{tempdir, NamedTempFile};

/**
 * 临时文件样例,
 * 此创建一个系统临时文件,并进行写入内容,读取临时文件内容。
 */
pub(crate) fn tempfile_sample() {
    // 系统临时目录
    let tmpdir = std::env::temp_dir();
    println!("temp dir location: {:?}", tmpdir);

    let currdir = std::env::current_dir().unwrap();

    println!("current dir: {:?}", currdir);

    // Write
    let mut tmpfile: File = tempfile::tempfile().unwrap();
    println!("tempfile : {:?}", tmpfile);

    write!(tmpfile, "Hello World!").unwrap();

    // Seek to start
    tmpfile.seek(SeekFrom::Start(0)).unwrap();

    // Read
    let mut buf = String::new();
    tmpfile.read_to_string(&mut buf).unwrap();
    assert_eq!("Hello World!", buf);
}

/**
 *
 */
pub(crate) fn temp_namedfile_sample() {
    let text = "Brian was here. Briefly.";

    let home_dir: std::path::PathBuf = env::home_dir().expect("Failed to get home directory");

    // Create a file inside of path  by `NamedTempFile::new_in(paht)`.
    let mut file1 = NamedTempFile::new_in(home_dir).unwrap();
    println!("tempfile : {:?}", { &file1 });

    // Re-open it.
    let mut file2 = file1.reopen().unwrap();

    // Write some test data to the first handle.
    file1.write_all(text.as_bytes()).unwrap();

    // Read the test data using the second handle.
    let mut buf = String::new();
    file2.read_to_string(&mut buf).unwrap();

    assert_eq!(buf, text);
}

/**
 * 临时目录创建,临时文件
 */
pub(crate) fn tempdir_addfile() {
    // Create a directory inside of `std::env::temp_dir()`.
    let dir = tempdir().unwrap();

    let file_path = dir.path().join("my-temporary-note.txt");

    let mut file = File::create(file_path).unwrap();

    writeln!(file, "Brian was here. Briefly.").unwrap();

    // By closing the `TempDir` explicitly, we can check that it has
    // been deleted successfully. If we don't close it explicitly,
    // the directory will still be deleted when `dir` goes out
    // of scope, but we won't know whether deleting the directory
    // succeeded.
    drop(file);
    dir.close().unwrap();
}

///
/// 单元测试
/// #[cfg(test)]
///
#[cfg(test)]
mod tests {
    // 注意这个惯用法:在 tests 模块中,从外部作用域导入所有名字。
    use super::*;

    #[test]
    fn test_tempfile() {
        tempfile_sample();

        tempdir_addfile();

        temp_namedfile_sample();
    }
}

}

CSV 文件操作

安装csv 文件cartes包

cargo add csv

或者修改Cargo.toml配置:

csv = "1.3.1"

样例代码:

#![allow(unused)]
fn main() {
use std::{error::Error, io, process};

use anyhow::Result;
use csv::Reader;
use csv::{ReaderBuilder, WriterBuilder};
use serde::{Deserialize, Serialize};

/// 定义 CSV 结构体 并使用 serde 进行序列化和反序列化
/// 使用 serde 的属性来指定字段名和映射
#[derive(Debug, Deserialize, Serialize)]
struct Employee {
    #[serde(rename = "ID")]
    id: u32,
    #[serde(rename = "Name")]
    name: String,
    #[serde(rename = "Age")]
    age: u8,
    #[serde(rename = "Department")]
    department: String,
    #[serde(rename = "Salary")]
    salary: f64,
}

/// 使用 serde 的属性来指定字段名和映射
#[derive(Debug, serde::Deserialize)]
struct Record {
    city: String,
    region: String,
    country: String,
    population: Option<u64>,
}

/// 处理 CSV 文件
fn process_employees(input_path: &str, output_path: &str) -> Result<()> {
    // 读取 CSV 文件 (自动推断分隔符)
    let mut reader = ReaderBuilder::new()
        .has_headers(true)
        .from_path(input_path)?;

    // 反序列化为 Employee 结构体
    let employees: Vec<Employee> = reader.deserialize().collect::<Result<_, _>>()?; // 收集时处理错误

    // 过滤数据: 保留薪资 > 5000 且年龄 < 50 的员工
    let filtered: Vec<_> = employees
        .into_iter()
        .filter(|e| e.salary > 5000.0 && e.age < 50)
        .collect();

    // 写入新 CSV
    let mut writer = WriterBuilder::new()
        .delimiter(b',')
        .has_headers(false)
        .from_path(output_path)?;

    // 写入表头
    writer.write_record(&["ID", "Name", "Age", "Department", "Salary"])?;

    // 序列化并写入数据
    for employee in filtered {
        //序列化并写入数据
        writer.serialize(employee)?;
    }

    writer.flush()?; // 确保数据写入磁盘
    Ok(())
}

/// 处理大型 CSV 文件
fn process_large_csv() -> Result<()> {
    // 读取 CSV 文件
    let mut reader = Reader::from_path("data/large_data.csv")?;

    // 逐行读取并处理
    // 使用迭代器逐行读取
    for result in reader.deserialize::<Employee>() {
        let employee: Employee = result?;
        // 逐条处理避免内存溢出
        println!("{}", employee.name)
    }

    Ok(())
}

/// 读取 CSV 文件并处理数据
fn csv_sample() -> Result<()> {
    // 输入输出文件路径
    let input = "data/employees.csv";
    let output = "data/filtered_employees.csv";

    // 处理数据
    process_employees(input, output)?;

    println!("CSV 处理完成!结果已保存至 {}", output);

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_csv_sample() {
        assert!(csv_sample().is_ok());
    }

    #[test]
    fn test_process_large_csv() {
        assert!(process_large_csv().is_ok());
    }
}

}

数据库

并发异步

声明宏

一个简单的声明宏(Macro), Say Hello

一个简单的声明宏(Macro),say_hello,用于打印一条消息。

#![allow(unused)]
fn main() {
macro_rules! say_hello {
    () => {
        println!("Hello from macro!");
    };
}

fn declare_macros_hello_sample() {
    say_hello!(); // 调用宏
}

}

带参数的声明宏

带参数的声明宏(Macro),make_vec,用于创建一个动态数组,并初始化值,与标准库的vec! 很像吧。

#![allow(unused)]

fn main() {
macro_rules! make_vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $( temp_vec.push($x); )*
            temp_vec
        }
    };
}

fn declare_macros_make_sample() {
    let v = make_vec!(1, 2, 3, 4);
    println!("{:?}", v); // [1, 2, 3, 4]
}

}

服务

序列化

JSON 序列化

使用 serde_json 库进行序列化和反序列化

安装serde 文件cartes包

cargo add serde
cargo add serde-transcode
cargo add serde_json

或者修改Cargo.toml配置:

serde = { version = "1.0.196", features = ["derive"] }
serde-transcode = "1.1.1"
serde_json = "1.0.113"

样例代码:

#![allow(unused)]
fn main() {
//!
//! JSON Sample
//!

use std::io;

use serde::{Deserialize, Serialize};
use serde_json::{Result, Value};

/**
 * parse untype JSON data
 * return Value HashMap
 */
pub(crate) fn untyped_sample() -> Result<()> {
    // Some JSON input data as a &str. Maybe this comes from the user.
    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;

    // Parse the string of data into serde_json::Value.
    let v: Value = serde_json::from_str(data)?;

    // Access parts of the data by indexing with square brackets.
    println!("Please call {} at the number {}", v["name"], v["phones"][0]);

    Ok(())
}

/// Struct Person
#[derive(Serialize, Deserialize)]
struct Person {
    name: String,
    age: u8,
    phones: Vec<String>,
}

/**
 *  Struct serialize to Json
 */
pub(crate) fn typed_sample() -> Result<()> {
    // Some JSON input data as a &str. Maybe this comes from the user.
    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;

    // Parse the string of data into a Person object. This is exactly the
    // same function as the one that produced serde_json::Value above, but
    // now we are asking it for a Person as output.
    let p: Person = serde_json::from_str(data)?;

    // Do things just like with any other Rust data structure.
    println!("Please call {} at the number {}", p.name, p.phones[0]);

    Ok(())
}

/**
 * JSON transcode
 */
pub(crate) fn json_transcode_sample() {
    // A JSON input with plenty of whitespace.
    let input = r#"
      {
        "a boolean": true,
        "an array": [3, 2, 1]
      }
    "#;

    // A JSON deserializer. You can use any Serde Deserializer here.
    let mut deserializer = serde_json::Deserializer::from_str(input);

    // A compacted JSON serializer. You can use any Serde Serializer here.
    let mut serializer = serde_json::Serializer::new(io::stdout());

    // Prints `{"a boolean":true,"an array":[3,2,1]}` to stdout.
    // This line works with any self-describing Deserializer and any Serializer.
    serde_transcode::transcode(&mut deserializer, &mut serializer).unwrap();
}

/// Point Struct
#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

/**
 * JSON process
 */
pub(crate) fn json_process_sample() {
    let point = Point { x: 1, y: 2 };

    // Convert the Point to a JSON string.
    let serialized = serde_json::to_string(&point).unwrap();

    // Prints serialized = {"x":1,"y":2}
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Point.
    let deserialized: Point = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized = Point { x: 1, y: 2 }
    println!("deserialized = {:?}", deserialized);
}

///
/// 单元测试
/// #[cfg(test)]
///
#[cfg(test)]
mod tests {
    // 注意这个惯用法:在 tests 模块中,从外部作用域导入所有名字。
    use super::*;

    #[test]
    fn test_json() {
        untyped_sample().unwrap();

        typed_sample().unwrap();

        json_transcode_sample();

        json_process_sample();
    }
}


}

精选实战

Database

  • SurrealDB

Services

Sequences

Message Queue

MQ Broker 和 Client 样例。

  • MQTT
    • Rumqtt: Rumqtt 是由 Rust 开发的支持 MQTT 3.1/5 协议规范的全功能的MQTT 的Broker 和 Client 。

数据库

服务

服务依赖注入(Dependency Injection)

简单的静态依赖注入

#![allow(unused)]
fn main() {
use std::{
    any::{Any, TypeId},
    collections::HashMap,
    sync::{Arc, RwLock},
};


pub trait AnyService: Any + Send + Sync + 'static {}
impl<T: Any + Send + Sync + 'static> AnyService for T {}


// 定义一个 Repository Trait
trait UserRepository: Any + Send + Sync {
    fn get_user(&self, id: u32) -> String;
}

// 实现一个具体的 Repository
struct InMemoryUserRepository;

impl UserRepository for InMemoryUserRepository {
    fn get_user(&self, id: u32) -> String {
        format!("User {} from InMemoryRepo", id)
    }
}

// 定义一个 Service,它依赖 UserRepository
struct UserService<R: UserRepository> {
    repo: R,
}

impl<R: UserRepository> UserService<R> {
    fn new(repo: R) -> Self {
        Self { repo }
    }

    fn greet_user(&self, id: u32) -> String {
        let user = self.repo.get_user(id);
        format!("Hello, {}!", user)
    }
}

 // di 通常与异步运行时配合,但这里可以不用 async
 fn dependency_injection_manul_sample() -> anyhow::Result<()> {
    // --- 创建 DI 容器 ---
    let mut services = HashMap::<TypeId, Arc<dyn Any + Send + Sync>>::new();

    services.insert(
        TypeId::of::<InMemoryUserRepository>(),
        Arc::new(InMemoryUserRepository),
    );

    let repo = services
        .get(&TypeId::of::<InMemoryUserRepository>())
        .unwrap();

    let repo = (repo.clone()).downcast::<InMemoryUserRepository>().unwrap();

    services.insert(
        TypeId::of::<UserService<InMemoryUserRepository>>(),
        Arc::new(UserService::new(InMemoryUserRepository)),
    );

    //  --- 从容器中解析出 UserService ---

    let user_serivce = services
        .get(&TypeId::of::<UserService<InMemoryUserRepository>>())
        .unwrap();

    let user_service = user_serivce
        .clone()
        .downcast::<UserService<InMemoryUserRepository>>()
        .unwrap();

    // // 调用业务方法
    let greeting = user_service.greet_user(42);
    println!("{}", greeting);

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_dependency_injection_sample() {
        dependency_injection_manul_sample();
    }
}


}

执行输出结果:

running 1 test
test services::dependency_injection_concrete_sample::tests::test_dependency_injection_sample ... ok

successes:

---- services::dependency_injection_concrete_sample::tests::test_dependency_injection_sample stdout ----
Hello, User 42 from InMemoryRepo!


successes:
    services::dependency_injection_concrete_sample::tests::test_dependency_injection_sample

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 18 filtered out; finished in 0.00s

服务容器的动态依赖注入

#![allow(unused)]
fn main() {
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

// Base trait for all services
trait Service: Send + Sync + 'static {
    fn name(&self) -> &'static str;
}

// Example service interfaces
trait LoggerService: Send + Sync + 'static {
    fn log(&self, message: &str);
}

trait DatabaseService: Send + Sync + 'static {
    fn query(&self, query: &str) -> String;
}

// Concrete service implementations
struct ConsoleLogger;
impl Service for ConsoleLogger {
    fn name(&self) -> &'static str {
        "ConsoleLogger"
    }
}
impl LoggerService for ConsoleLogger {
    fn log(&self, message: &str) {
        println!("Log: {}", message);
    }
}

struct InMemoryDatabase;
impl Service for InMemoryDatabase {
    fn name(&self) -> &'static str {
        "InMemoryDatabase"
    }
}
impl DatabaseService for InMemoryDatabase {
    fn query(&self, query: &str) -> String {
        format!("Query result for: {}", query)
    }
}

// Service with dependencies
struct BusinessService {
    logger: Arc<dyn LoggerService>,
    database: Arc<dyn DatabaseService>,
}
impl Service for BusinessService {
    fn name(&self) -> &'static str {
        "BusinessService"
    }
}
impl BusinessService {
    fn new(logger: Arc<dyn LoggerService>, database: Arc<dyn DatabaseService>) -> Self {
        BusinessService { logger, database }
    }

    fn perform_task(&self, task: &str) {
        self.logger.log(&format!("Performing task: {}", task));
        let result = self.database.query(task);
        self.logger.log(&format!("Task result: {}", result));
    }
}

// Service Container
struct ServiceContainer {
    services: Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
    factories: Mutex<HashMap<TypeId, Box<dyn Fn(&ServiceContainer) -> Arc<dyn Any + Send + Sync>>>>,
}

impl ServiceContainer {
    fn new() -> Self {
        ServiceContainer {
            services: Mutex::new(HashMap::new()),
            factories: Mutex::new(HashMap::new()),
        }
    }

    // Register a concrete service
    fn register<T: Service + 'static>(&self, service: T) {
        let type_id = TypeId::of::<T>();
        let service: Arc<dyn Any + Send + Sync> = Arc::new(service);
        self.services.lock().unwrap().insert(type_id, service);
    }

    // Register a trait object
    fn register_trait<T: ?Sized + Any + Send + Sync + 'static>(
        &self,
        service: Arc<dyn Any + Send + Sync>,
        type_id: TypeId,
    ) {
        // Ensure the service is a trait object
        // Insert the service into the container
        let service: Arc<dyn Any + Send + Sync> = service;
        // let type_id = TypeId::of::<T>();

        println!("Registering trait object: {:?}", type_id);

        // Insert the service into the services map
        self.services.lock().unwrap().insert(type_id, service);
    }

    // Register a factory for lazy initialization
    fn register_factory<
        T: Service + 'static,
        F: Fn(&ServiceContainer) -> Arc<T> + Send + Sync + 'static,
    >(
        &self,
        factory: F,
    ) {
        let type_id = TypeId::of::<T>();
        let wrapped_factory: Box<dyn Fn(&ServiceContainer) -> Arc<dyn Any + Send + Sync>> =
            Box::new(move |container| {
                let service = factory(container);
                service as Arc<dyn Any + Send + Sync>
            });
        self.factories
            .lock()
            .unwrap()
            .insert(type_id, wrapped_factory);
    }

    // Resolve a concrete service
    fn resolve<T: Service + 'static>(&self) -> Option<Arc<T>> {
        let type_id = TypeId::of::<T>();
        if let Some(service) = self.services.lock().unwrap().get(&type_id) {
            return service.clone().downcast::<T>().ok();
        }
        if let Some(factory) = self.factories.lock().unwrap().get(&type_id) {
            let service = factory(self);
            self.services
                .lock()
                .unwrap()
                .insert(type_id, service.clone());
            return service.downcast::<T>().ok();
        }
        None
    }

    // Resolve a trait object (accepts Arc<dyn Trait> types); trait objects themselves (dyn Trait) are unsized,
    // so call this with Arc<dyn Trait> as the type parameter (e.g. resolve_trait::<Arc<dyn LoggerService>>()).
    fn resolve_trait<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
        let type_id = TypeId::of::<T>();

        println!("resolve trait trait object: {:?}", type_id);
        if let Some(service) = self.services.lock().unwrap().get(&type_id) {
            let downcast = service.clone().downcast::<T>().ok();

            println!("resolve trait trait object: {:?}", downcast.is_some());

            return downcast;
        }

        None
    }
}

fn container_injection_main() {
    let container = Arc::new(ServiceContainer::new());

    // Register concrete services
    container.register(ConsoleLogger);
    container.register(InMemoryDatabase);

    // Register trait objects
    // using a box or reference package to dyn trait objects
    container.register_trait::<Arc<dyn LoggerService>>(
        Arc::new(Arc::new(ConsoleLogger) as Arc<dyn LoggerService>),
        TypeId::of::<Arc<dyn LoggerService>>(),
    );

    container.register_trait::<Arc<dyn DatabaseService>>(
        Arc::new(Arc::new(InMemoryDatabase) as Arc<dyn DatabaseService>),
        TypeId::of::<Arc<dyn DatabaseService>>(),
    );

    // Register a factory for BusinessService (resolve concrete implementations and coerce to trait objects)
    container.register_factory::<BusinessService, _>(|container| {
        let logger_concrete = container
            .resolve::<ConsoleLogger>()
            .expect("LoggerService not found");
        let logger: Arc<dyn LoggerService> = logger_concrete;

        let database_concrete = container
            .resolve::<InMemoryDatabase>()
            .expect("DatabaseService not found");
        let database: Arc<dyn DatabaseService> = database_concrete;

        Arc::new(BusinessService::new(logger, database))
    });

    // Resolve and use BusinessService
    let business_service = container
        .resolve::<BusinessService>()
        .expect("BusinessService not found");
    business_service.perform_task("Process data");

    // Resolve and use a trait object (request Arc<dyn LoggerService> as the type parameter)
    let logger = container
        .resolve_trait::<Arc<dyn LoggerService>>()
        .expect("LoggerService not found");
    logger.log("Direct logger access");

    // Resolve and use a trait object (request Arc<dyn DatabaseService> as the type parameter)
    let database_service = container
        .resolve_trait::<Arc<dyn DatabaseService>>()
        .expect("DatabaseService not found");
    let result = database_service.query("Direct database access");

    println!("query: {}", result);
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_injection_main() {
        container_injection_main();
    }
}


}

执行输出结果:


running 1 test
test services::dependency_injection_dynmaic_sample::tests::test_injection_main ... ok

successes:

---- services::dependency_injection_dynmaic_sample::tests::test_injection_main stdout ----
Registering trait object: TypeId(0x56cbfa3cabf188feb9d956576e38424a)
Registering trait object: TypeId(0x8ffe278beee70f55493c3bb2f23af8b3)
Log: Performing task: Process data
Log: Task result: Query result for: Process data
resolve trait trait object: TypeId(0x56cbfa3cabf188feb9d956576e38424a)
resolve trait trait object: true
Log: Direct logger access
resolve trait trait object: TypeId(0x8ffe278beee70f55493c3bb2f23af8b3)
resolve trait trait object: true
query: Query result for: Direct database access


successes:
    services::dependency_injection_dynmaic_sample::tests::test_injection_main

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 18 filtered out; finished in 0.00s

插件(Plugin)

如何实现一个可扩展的插件系统,在 rust中,可以使用 inventory crates 实现类似的目标。

样例代码:

#![allow(unused)]
fn main() {
use inventory::submit;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

// Define a trait for inventory operations
trait InventoryOp: Send + Sync {
    fn name(&self) -> &'static str;
    fn execute(&self, inventory: &Mutex<HashMap<String, u32>>, item: &str, quantity: u32);
}

// Structure for registry
#[derive(Clone, Copy)]
struct InventoryPlugin {
    name: &'static str,
    handler: &'static dyn InventoryOp,
}

inventory::collect!(InventoryPlugin);

// Plugin: Empty Operation
struct EmptyOp;
impl InventoryOp for EmptyOp {
    fn name(&self) -> &'static str {
        ""
    }
    fn execute(&self, _inventory: &Mutex<HashMap<String, u32>>, _item: &str, _quantity: u32) {}
}

inventory::submit! {
    InventoryPlugin {
        name: "",
        handler: &EmptyOp,
    }
}

// Plugin 1: Add Item
struct AddItem;
impl InventoryOp for AddItem {
    fn name(&self) -> &'static str {
        "add"
    }
    fn execute(&self, inventory: &Mutex<HashMap<String, u32>>, item: &str, quantity: u32) {
        let mut inv = inventory.lock().unwrap();
        let current = inv.get(item).copied().unwrap_or(0);
        inv.insert(item.to_string(), current + quantity);
        println!("Added {} {} to inventory", quantity, item);
    }
}

inventory::submit! {
    InventoryPlugin {
        name: "add",
        handler: &AddItem,
    }
}

// Plugin 2: Remove Item
struct RemoveItem;
impl InventoryOp for RemoveItem {
    fn name(&self) -> &'static str {
        "remove"
    }
    fn execute(&self, inventory: &Mutex<HashMap<String, u32>>, item: &str, quantity: u32) {
        let mut inv = inventory.lock().unwrap();
        if let Some(current) = inv.get_mut(item) {
            if *current >= quantity {
                *current -= quantity;
                println!("Removed {} {} from inventory", quantity, item);
            } else {
                println!("Error: Not enough {} in inventory", item);
            }
        } else {
            println!("Error: {} not found in inventory", item);
        }
    }
}

inventory::submit! {
    InventoryPlugin {
        name: "remove",
        handler: &RemoveItem,
    }
}

fn inventory_main() {
    // Thread-safe inventory
    let inventory = Arc::new(Mutex::new(HashMap::<String, u32>::new()));
    inventory.lock().unwrap().insert("apple".to_string(), 10);

    // Collect plugins
    let mut plugins: HashMap<&'static str, &'static dyn InventoryOp> = HashMap::new();

    for plugin in inventory::iter::<InventoryPlugin> {
        if !plugin.name.is_empty() {
            plugins.insert(plugin.name, plugin.handler);
        }
    }

    // Simulate operations
    let ops = vec![
        ("add", "apple", 5),
        ("remove", "apple", 3),
        ("add", "sword", 2),
        ("remove", "apple", 20),
    ];

    for (op_name, item, quantity) in ops {
        if let Some(op) = plugins.get(op_name) {
            op.execute(&inventory, item, quantity);
        } else {
            println!("Unknown operation: {}", op_name);
        }
    }

    // Print final inventory
    println!("Final inventory:");
    let inv = inventory.lock().unwrap();
    for (key, quantity) in inv.iter() {
        println!("{}: {}", key, quantity);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_inventory_main() {
        inventory_main();
    }
}



}

序列ID

消息队列

模板

Algo Sample

LeetCode Solution Sample