This commit is contained in:
nap.liu
2023-12-11 18:40:53 +08:00
parent d1b95f1096
commit 5dc4708fe2
79 changed files with 2269 additions and 39 deletions

7
14.Generics/14. generics/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "generics"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "generics"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,46 @@
//!
//! 泛型Generics
//!
//! 泛型是将类型和功能可以用在更多的类型上的一个能力,
//! 它可以通过多种方式非常有效的缩减重复代码,但是带来的问题是会让使用方的语法更复杂。
//!
//! 作为泛型需要非常精确的指定泛型可接受的类型范围。
//! 泛型最简单常用的地方就是当做函数的参数使用了。
//!
//! 泛型函数可以通过 `<T>` (T可以是任意的名称)来声明接收泛型类型,泛型类型需要使用大驼峰命名 `<Aaa, Bbb, ...>`
//! 如果一个函数声明了泛型参数,并且参数指明使用了泛型中声明的类型,则该函数就是泛型函数,
//! 没有使用泛型声明的参数则是具体类型。
//!
//! 下面这个就是一个泛型的函数
//! ```r
//! fn foo<f>(arg: T) { ... }
//! ```
//! 该函数定义了一个泛型 `T`,声明参数 `arg` 为泛型 `T`。
//! 这个函数可以接受任意类型的参数。
//!
// 一个具体类型 `A`
struct A;
// 定义一个类型 `Single`,该类型拥有一个元组(Tuple),这个元组里面有一个类型 `A` (这个 `A` 类型就是上面定义的类型)
struct Single(A);
// ^ 这里定义元组内只包含一个 `A` 类型
// 这里声明了一个泛型 `<T>`,表明 `SingleGen<T>` 类型是一个泛型的类型,
// 然后这个泛型 `T`,放在了元组中。
// 这里泛型 `<T>` 中的 `T` 可以是任意的类型,包含上面定义的 `A`
struct SingleGen<T>(T);
fn main() {
// `Single` 类型需要一个类型 `A`,因为 `Single` 是具体类型的类型,不是泛型。
let _s = Single(A);
// 创建一个变量 `_char` 明确指明类型是 `SingleGen<char>`
// 这里通过 `SingleGen('a')` 明确的向泛型传递了一个 `char` 型的参数。
let _char: SingleGen<char> = SingleGen('a');
// `SingleGen` 也可以使用任意的其他类型,泛型可以自动根据参数推断具体的类型。
let _t = SingleGen(A); // `A` 是上面定义的类型。
let _i32 = SingleGen(6); // 使用 `i32` 类型。
let _char = SingleGen('a'); // 使用 `char` 类型。
}

7
14.Generics/14.1 Functions/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "functions"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "functions"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,41 @@
//!
//! 泛型函数
//!
//! 泛型的相关规则也可以直接使用在函数上,当一个类型 `T` 被声明成了 `<T>`,则该类型就是泛型。
//!
//! 如果编译器不能推断出泛型的参数或者泛型的返回值的情况下,需要明确的指出泛型的类型。
//!
struct A; // 具体类型 `A`
struct S(A); // 具体类型 `S`
struct SGen<T>(T); // 泛型类型 `SGen`.
// 下面这些函数都会直接转移变量的所有权,一旦调用了函数,变量就被转移到函数中了,而函数中什么都没做
// 当函数结束的时候会直接回收掉所有传入的参数。
// `reg_fn` 函数接受一个具体的类型 `S`,所以该函数不是泛型的。
fn reg_fn(_s: S) {}
// `gen_spec_t` 函数接受一个 `SGen<A>`,虽然 `SGen` 是泛型,
// 但是我们手动指定了 `SGen` 的类型为 `A`,所以该函数也不是泛型的
fn gen_spec_t(_s: SGen<A>) {}
// 这里同上,因为手动指定了 `SGen` 的类型是 `i32` 所以该函数也不是泛型的。
fn gen_spec_i32(_s: SGen<i32>) {}
// `generic` 函数通过 <T> 声明了一个泛型 `T` 的类型,然后把这个泛型 `T`,传递给了 `SGen`
// 所以该函数是一个拥有一个泛型类型的泛型函数。
fn generic<T>(_s: SGen<T>) {}
fn main() {
// 使用非泛型的函数
reg_fn(S(A)); // 具体类型
gen_spec_t(SGen(A)); // 隐式指定 `SGen` 的泛型类型为 `A`。
gen_spec_i32(SGen(6)); // 隐式指定 `SGen` 的泛型类型为 `i32`。
// 手动明确指定函数的泛型类型为 `char` 类型
generic::<char>(SGen('a'));
// 隐式指定泛型类型为 `char` 类型
generic(SGen('c'));
}

View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "implementation"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "implementation"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,45 @@
//!
//! 泛型实现implementation
//!
//! 和函数类似,实现也可以使用泛型
//!
//! ```
//! struct S; // 具体类型 `S`
//! struct GenericVal<T>(T); // 泛型 `GenericVal`
//
//! // 使用具体类型实现 GenericVal 类型
//! impl GenericVal<f32> {} // 对于 `f32` 类型的实现
//! impl GenericVal<S> {} // 对于 `S` 类型的实现
//!
//! // `<T>` 继续保持示范性,也就是任意类型的实现
//! impl<T> GenericVal<T> {}
//! ```
//!
struct Val {
val: f64,
}
struct GenVal<T> {
gen_val: T,
}
// 具体类型的实现
impl Val {
fn value(&self) -> &f64 {
&self.val
}
}
// 泛型类型对于任意的类型的实现
impl<T> GenVal<T> {
fn value(&self) -> &T {
&self.gen_val
}
}
fn main() {
let x = Val { val: 3.0 };
let y = GenVal { gen_val: 3i32 };
println!("{}, {}", x.value(), y.value());
}

7
14.Generics/14.3 Traits/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "traits"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "traits"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,34 @@
//!
//! 泛型特性 (trait)
//!
//! 特性也可以是泛型的,下面是一个泛型特性的示例。
//!
struct Empty;
struct Null;
// 定义一个泛型的特性
trait DoubleDrop<T> {
// 函数会转移实例的所有权,并且接收的参数也会转移所有权,
// 当函数执行以后,会自动移除实例和传入的参数
fn double_drop(self, _: T);
}
// 声明两个泛型类型 `T`、`U`,然后为泛型 `U` 实现上面定义的泛型 `DoubleDrop` 特性
// 因为泛型 `T` 和 `U` 都没有明确的类型也就是可以是任意的类型,也就是说这个实现会对所有的类型都生效!!!
impl<T, U> DoubleDrop<T> for U {
// 该函数转移实例和参数的所有权,然后直接丢弃,这样就实现了 `drop` 的功能。
fn double_drop(self, _: T) {}
}
fn main() {
let empty = Empty;
let null = Null;
// 丢弃了 `empty` 和 `null` 两个实例。
empty.double_drop(null);
// empty;
// null;
// ^ TODO: 尝试移除注释
}

7
14.Generics/14.4 Bounds/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bounds"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "bounds"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,112 @@
//!
//! 泛型约束Bounds
//!
//! 再使用泛型的时候,很多时候都会需要要求泛型必须拥有哪些特性,如果不能满足的话不能使用该函数,
//! 所有就有了泛型的约束,要求传递的参数必须实现某些特定的特性。
//!
fn example01() {
use std::fmt::Display;
// 该函数定义了一个泛型类型 `T`,并且约束该类型必须实现要先实现 `Display` 特性。
fn printer<T: Display>(t: T) {
println!("{}", t);
}
struct S<T: Display>(T);
// 错误! `Vec<T>` 没有实现 `Display` 特性
// let s = S(vec![1]);
// 移除注释查看错误
}
// 导入格式化标签 `{:?}` 必须要实现的特性。
use std::fmt::Debug;
trait HasArea {
fn area(&self) -> f64;
}
impl HasArea for Rectangle {
fn area(&self) -> f64 {
self.length * self.height
}
}
#[derive(Debug)]
struct Rectangle {
length: f64,
height: f64,
}
#[allow(dead_code)]
struct Triangle {
length: f64,
height: f64,
}
// 泛型 `T` 必须实现 `Debug` 特性
fn print_debug<T: Debug>(t: &T) {
println!("{:?}", t);
}
// 泛型 `T` 必须实现 `HasArea` 特性,只有实现了这个特性才能使用 `HasArea` 上的 `area` 方法。
fn area<T>(t: &T) -> f64
where
// 另外一种泛型约束的语法,这样可以保持函数签名是干净的
T: HasArea,
{
t.area()
}
/// 即使是空的特性也可以用作约束,就像是标准库提供的 `Copy`、`Eq` 一样
fn testcase_empty_bounds() {
struct Cardinal;
struct BlueJay;
struct Turkey;
trait Red {}
trait Blue {}
impl Red for Cardinal {}
impl Blue for BlueJay {}
// 这个函数只允许实现了 `Red` 特性的参数调用。
fn red<T: Red>(_: &T) -> &'static str {
"red"
}
// 这个函数只允许实现了 `Blue` 特性的参数调用。
fn blue<T: Blue>(_: &T) -> &'static str {
"blue"
}
let cardinal = Cardinal;
let blue_jay = BlueJay;
let _turkey = Turkey;
// `red()` 不能传递 `blue_jay`,因为函数进行了约束,反过来也一样。
println!("A cardinal is {}", red(&cardinal));
println!("A blue jay is {}", blue(&blue_jay));
// println!("A turkey is {}", red(&_turkey));
// ^ TODO: 移除注释查看错误
}
fn main() {
let rectangle = Rectangle {
length: 3.0,
height: 4.0,
};
let _triangle = Triangle {
length: 3.0,
height: 4.0,
};
print_debug(&rectangle);
println!("Area: {}", area(&rectangle));
// print_debug(&_triangle);
// println!("Area: {}", area(&_triangle));
// ^ TODO: 移除注释查看错误
// | Error: 这两个都没有实现相关的 `Debug` 或 `HasArea` 特性。
}

View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "multiple_bounds"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "multiple_bounds"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,30 @@
//!
//! 多重泛型约束
//! 多重约束可以在每个约束之间使用 `+` 号连接,
//! 多个泛型之间使用 `,` 号间隔
//!
//! 同样支持在 `<T: bound + bound2>` 或者 where `T: bound + bound2` 中使用
//!
use std::fmt::{Debug, Display};
fn compare_prints<T: Debug + Display>(t: &T) {
println!("Debug: `{:?}`", t);
println!("Display: `{}`", t);
}
fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) {
println!("t: `{:?}`", t);
println!("u: `{:?}`", u);
}
fn main() {
let string = "words";
let array = [1, 2, 3];
let vec = vec![1, 2, 3];
compare_prints(&string);
// compare_prints(&array);
// TODO ^ 移除注释查看错误
compare_types(&array, &vec);
}

View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "where_clauses"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "where_clauses"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,29 @@
//!
//! where 子语句
//! 在声明泛型类型的时候可以通过 `where` 子语句把泛型约束从签名中提取出来,
//! 这样可以让声明看起来更简洁一些,而且 `where` 子语句可以限定任意的类型,而不仅仅是函数参数。
//!
use std::fmt::Debug;
trait PrintInOption {
fn print_in_option(self);
}
// 这里使用 `where` 子语句重新定义了 `T` 类型为 `Option<T>`。
//
impl<T> PrintInOption for T
where
Option<T>: Debug, // 这里使用了类型的重定义,把 `T` 重新定义成 `Option<T>` 然后对对重定义的类型进行约束
{
// 这里的 `self` 就是 `T` 的实例,然后使用 `Some` 包装 `self` 就可以打印了,
// 因为 `Some` 实现了 `Debug` 特性。
fn print_in_option(self) {
println!("{:?}", Some(self));
}
}
fn main() {
let vec = vec![1, 2, 3];
vec.print_in_option();
}

View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "new_type_idiom"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "new_type_idiom"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,41 @@
//!
//! 新类型 newtype
//!
//! 新类型是指使用元组结构体包装一个其他的类型,然后基于新包装的类型实现类型的扩展或约束,
//! 这样的好处是可以让 `api` 更加清晰,不会造成歧义。
//!
struct Years(i64);
struct Days(i64);
impl Years {
pub fn to_days(&self) -> Days {
Days(self.0 * 365)
}
}
impl Days {
/// 舍弃不足的年份
pub fn to_years(&self) -> Years {
Years(self.0 / 365)
}
}
fn old_enough(age: &Years) -> bool {
age.0 >= 18
}
fn main() {
let age = Years(5);
let age_days = age.to_days();
println!("Old enough {}", old_enough(&age));
println!("Old enough {}", old_enough(&age_days.to_years()));
// println!("Old enough {}", old_enough(&age_days));
{
let years = Years(42);
let years_as_primitive_1: i64 = years.0; // 访问元组数据
let Years(years_as_primitive_2) = years; // 解构元组数据
}
}

View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "associated_items"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "associated_items"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,122 @@
//!
//! 关联项是指与各种类型的项目有关的一组规则,
//! 他是泛型特征的扩展,允许泛型特征在内部自定义新的项。
//!
//! 其中有一种成为关联类型,也就是实现特征的时候需要明确指定特征上定义的项的类型。
//!
fn the_problem() {
struct Container(i32, i32);
// 该特性明确要求需要传递 `<A, B>` 两个泛型类型,
// 还有两个方法可以获取 `first``last` 两个值。
trait Contains<A, B> {
fn contains(&self, _: &A, _: &B) -> bool; // 明确指明需要泛型 `A` `B`
fn first(&self) -> i32; // 不需要泛型 `A` 或 `B`.
fn last(&self) -> i32; // 不需要泛型 `A` 或 `B`.
}
// 明确声明要实现的类型是 `Contains<i32, i32>`,这样就代表泛型特性的 `<A, B>` 就是 `<i32, i32>`
impl Contains<i32, i32> for Container {
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
fn first(&self) -> i32 {
self.0
}
fn last(&self) -> i32 {
self.1
}
}
// 问题是这里的三个泛型的之间的关系,
// 这里 C 被约束为 Contains<A, B>, 而我们写约束的时候很麻烦。
fn difference<A, B, C>(container: &C) -> i32
where
C: Contains<A, B>,
{
container.last() - container.first()
}
let number_1 = 3;
let number_2 = 10;
let container = Container(number_1, number_2);
println!(
"Does container contain {} and {}: {}",
&number_1,
&number_2,
container.contains(&number_1, &number_2)
);
println!("First number: {}", container.first());
println!("Last number: {}", container.last());
println!("The difference is: {}", difference(&container));
}
fn associated_types() {
struct Container(i32, i32);
// 还是相同的定义,只不过这里不再使用泛型。
trait Contains {
// 定义两个通用的关联类型 `A``B`,这个时候还不知道关联类型的具体类型是什么,
// 具体可能是什么类型是由实现该特性的地方定义的。
type A;
type B;
// 这里 `Self::A` 中的 `Self` 实际上是指的 `Contains`,也就是当前的特性。
// 到实际的实现的时候这个 `Self` 会被替换成实现该特性的实际类型。
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
fn first(&self) -> i32;
fn last(&self) -> i32;
}
impl Contains for Container {
// 明确指定特性中定义的关联类型的具体类型。
type A = i32;
type B = i32;
// 这里的 `number_1: &i32, number_2: &i32` 可以被替换成 `number_1: &Self::A, number_2: &Self::B`,
// 因为 `Self` 等同于 `Container`
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
fn first(&self) -> i32 {
self.0
}
fn last(&self) -> i32 {
self.1
}
}
// 这里进行约束的时候就不需要额外的声明 `Contains` 特性所需要的两个泛型类型了。
fn difference<C: Contains>(container: &C) -> i32 {
container.last() - container.first()
}
let number_1 = 3;
let number_2 = 10;
let container = Container(number_1, number_2);
println!(
"Does container contain {} and {}: {}",
&number_1,
&number_2,
container.contains(&number_1, &number_2)
);
println!("First number: {}", container.first());
println!("Last number: {}", container.last());
println!("The difference is: {}", difference(&container));
}
fn main() {
the_problem();
associated_types();
}

View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "phantom_type_parameters"
version = "0.1.0"

View File

@@ -0,0 +1,8 @@
[package]
name = "phantom_type_parameters"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,58 @@
//!
//! 幽灵类型 Phantom type
//!
//! 幽灵类型的特性是只存在于编译阶段用于辅助编译器推断类型和强制保持某些变量,属性的生命周期。
//!
//! 有很多时候需要和外部的三方库交换数据,这时候我们把不安全的代码封装到安全的结构或者代码中的时候,
//! 需要交换很多数据,这些数据由三方的库申请的,这时候编译器并不知道这个数据的生命周期,所以需要这个
//! 幽灵类型来明确的告知编译器这个类型的生命周期,这样编译器才能通过编译检查。
//!
//! 关于幽灵类型更加详细的解释可以看[这里](https://doc.rust-lang.org/nomicon/phantom-data.html)
//!
//! 幽灵定义在泛型中,通过标准库导出的特殊类型 `std::marker::PhantomData` 来标记一个幽灵类型,
//! 该类型只用于编译器的类型检查,并不占用空间,也不能用于存放数据,也没有运行时的行为。
//!
//!
use std::marker::PhantomData; // 导入幽灵类型
// 声明一个拥有幽灵类型的元组结构,两个泛型 `<A, B>` 泛型 `A` 拥有是常规的类型,泛型 `B` 是一个隐藏的幽灵类型。
#[derive(PartialEq)] // 允许结构直接行进 `==` 运算
struct PhantomTuple<A, B>(A, PhantomData<B>);
// 声明一个拥有幽灵类型的结构体,两个泛型 `<A, B>` 泛型 `A` 拥有是常规的类型,泛型 `B` 是一个隐藏的幽灵类型。
#[derive(PartialEq)] // 允许结构直接行进 `==` 运算
struct PhantomStruct<A, B> {
first: A,
phantom: PhantomData<B>,
}
// 注意:代码编译的时候只会为正常的泛型 `A` 申请空间,而幽灵类型 `B` 没有空间,
// 所以使用了幽灵类型 `B` 的字段或者位置都不能当做实际存在的类型使用。
fn main() {
// 这里的 `f32` 和 `f64` 类型是给幽灵类型使用的。
// PhantomTuple type specified as `<char, f32>`.
// 明确指定泛型的数据类型,这里声明类型为 `<char, f32>`
let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData);
// 明确指定泛型的数据类型,这里声明类型为 `<char, f64>`
let _tuple2: PhantomTuple<char, f64> = PhantomTuple('Q', PhantomData);
// 明确指明类型 `<char, f32>`
let _struct1: PhantomStruct<char, f32> = PhantomStruct {
first: 'Q',
phantom: PhantomData,
};
// 明确指明类型 `<char, f64>`
let _struct2: PhantomStruct<char, f64> = PhantomStruct {
first: 'Q',
phantom: PhantomData,
};
// 编译错误!类型匹配不能对比
// println!("_tuple1 == _tuple2 yields: {}", _tuple1 == _tuple2);
// 编译错误!类型匹配不能对比
// println!("_struct1 == _struct2 yields: {}", _struct1 == _struct2);
}