跳转至

Rust-进阶

1 泛型Generics

**rust泛型也就是C++中的模板**。rust盒C++中对泛型的要求有一些不同,如下:
1. rust种**方法、函数的泛型必须要有类型限制**,比如,方法种对泛型进行的加法运算,就要给泛型添加`std::ops::Add`的限制。C++种可以加这方面的限制,也可以不加。
2. rust中**结构、枚举泛型则不需要加限制**。

rust 中泛型参数可以用任意有效的符号代替,但通常使用 TU 等代替。

1.1 函数泛型

  • 示例
#![allow(unused)]
fn main() {
    fn add<T: std::ops::Add<Output = T>>(a:T, b:T) -> T {
        a + b
    }
    println!("{}",add(2,3));
}
  • 可能的输出
5

1.2 结构泛型

  • 示例
struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

1.3 枚举泛型

  • 示例
enum Option<T> {
    Some(T),
    None,
}

1.4 方法泛型

方法泛型需要注意的是使用 impl<T>

struct Point<T> {
    x: T,
    y: T,
}
impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}
fn main() {
    let p = Point { x: 5, y: 10 };
    println!("p.x = {}", p.x());
}

1.5 泛型的具体化

这和**C++中模板的显式具体化**相同,为特点的类型定义规则。泛型会**优先匹配具体化**的实现。
#![allow(unused)]
struct Point<T> {
    x: T,
    y: T,
}
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}
fn main() {
    let p1:Point<f32> = Point{x:2.0,y:3.0};
    println!("{}",p1.distance_from_origin());
}

1.6 const 泛型

rust中const泛型盒C++中的基本类型模板形参相同。泛型形参可以指定整形类型。
fn display_array<T: std::fmt::Debug, const N: usize>(arr: [T; N]) {
    println!("{:?}", arr);
}
fn main() {
    let arr: [i32; 3] = [1, 2, 3];
    display_array(arr);

    let arr: [i32; 2] = [1, 2];
    display_array(arr);
}
  • 可能的输出
[1, 2, 3]  
[1, 2]

1.7 const 泛型表达式

rust的泛型表达式和C++20中的`concepts` 和 `requires`很像,也是编译器检测泛型参数是否满足表达式。

2 集合

3 特征 Trait

4 包管理

  • Package:可以用来构建、测试和分享包
  • WorkSpace:对于大型项目,可以进一步将多个Crate联合在一起,组织成WorkSpace
  • Crate:一个由多个Module组成的树形结构,可以作为三方库进行分发,也可以生成可执行文件进行运行
  • Module:可以一个文件多个模块,也可以一个文件一个模块,模块可以被认为是真实项目中的代码组织单元

一个真实项目中典型的 Package,会包含多个二进制包,这些包文件被放在 src/bin 目录下,每一个文件都是独立的二进制包,同时也会包含一个库包,该包只能存在一个 src/lib.rs

.
├── 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.rssrc/bin/main2.rs,它们会分别生成一个文件同名的二进制可执行文件
  • 集成测试文件:tests 目录下
  • 基准性能测试 benchmark 文件:benches 目录下
  • 项目示例:examples 目录下

4.1 mod

模块可以嵌套,也可以放在同一个文件,也可以分离到各个文件

4.1.1 Module 的声明

rust 中的Module通过关键字 mod 模组名{...} 来声明,这种声明方式和 C++中命名空间一样 namespace 命名空间名{...}

4.1.2 Module 的引入

mod 模组名 表示从和模组同名的文件加载该模块的内容, 这一点和 C++中 include 或者 C++20 中 Module一样。

4.2 pub

rust pub 相当于C++中的public。rust中默认是private。
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

4.3 use

rust 中 use 和 C++中 using namespace很像,简短了在使用外部模组下内容时长长的模组路径。

  • 示例
#![allow(unused)]
fn main() {
mod front_of_house;
// 使用front_of_house:hosting的作用域引入到当前文件中,可以直接使用其下的内容
pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}
}

4.3.1 use .. as 别名

作用:将引入的内容重新命名,避免引入的内容同名污染问题。

4.3.2 pub use

作用:当外部的模块项 A 被引入到当前模块中时,它的可见性自动被设置为私有的,如果你希望允许其它外部代码引用我们的模块项 A,那么可以对它进行再导出。

#![allow(unused)]
fn main() {
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}
}

4.4 self、super、crate

  • self 其实就是引用自身模块中的项
  • super 代表的是父模块为开始的引用方式,类似 ../
  • crate 相当于模组的根目录,用于绝对路径导入模组方式
#![allow(unused)]
fn main() {
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();
}
}

4.5 第三方包的引入

引入下第三方包中的模块操作步骤如下: 1. 修改 Cargo.toml 文件,在 [dependencies] 区域添加一行:rand = "0.8.3" 2. 此时,如果你用的是 VSCoderust-analyzer 插件,该插件会自动拉取该库,你可能需要等它完成后,再进行下一步(VSCode 左下角有提示)

好了,此时,rand 包已经被我们添加到依赖中,下一步就是在代码中使用:

use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1..101);
}