From 2cabfff8b90d1e5a3e15842b00d6ba857d0e911a Mon Sep 17 00:00:00 2001 From: Davirain Date: Wed, 17 May 2023 21:27:17 +0800 Subject: [PATCH] update --- src/SUMMARY.md | 36 ++- src/chapter_3/chapter_3_10.md | 5 + src/chapter_3/chapter_3_10_1.md | 509 ++++++++++++++++++++++++++++++++ src/chapter_3/chapter_3_10_2.md | 3 + src/chapter_3/chapter_3_10_3.md | 113 +++++++ src/chapter_3/chapter_3_11.md | 3 + src/chapter_3/chapter_3_12.md | 3 + src/chapter_3/chapter_3_16.md | 3 + src/chapter_3/chapter_3_17.md | 3 + src/chapter_3/chapter_3_18.md | 3 + src/chapter_3/chapter_3_19.md | 3 + src/chapter_3/chapter_3_22.md | 3 + src/chapter_3/chapter_3_23.md | 3 + src/chapter_3/chapter_3_9.md | 1 - 14 files changed, 678 insertions(+), 13 deletions(-) create mode 100644 src/chapter_3/chapter_3_10.md create mode 100644 src/chapter_3/chapter_3_10_1.md create mode 100644 src/chapter_3/chapter_3_10_2.md create mode 100644 src/chapter_3/chapter_3_10_3.md create mode 100644 src/chapter_3/chapter_3_11.md create mode 100644 src/chapter_3/chapter_3_12.md create mode 100644 src/chapter_3/chapter_3_16.md create mode 100644 src/chapter_3/chapter_3_17.md create mode 100644 src/chapter_3/chapter_3_18.md create mode 100644 src/chapter_3/chapter_3_19.md create mode 100644 src/chapter_3/chapter_3_22.md create mode 100644 src/chapter_3/chapter_3_23.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 80a32ce..93cfc1c 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -15,18 +15,30 @@ - [Slice类型](./chapter_3/chapter_3_7_3.md) - [复合数据类型](./chapter_3/chapter_3_8.md) - [泛型](./chapter_3/chapter_3_9.md) - - [3.13 闭包](./chapter_3/chapter_3_13.md) - - [3.14 迭代器](./chapter_3/chapter_3_14.md) - - [3.15 常见Collections](./chapter_3/chapter_3_15.md) - - [3.15.1 Vector](./chapter_3/chapter_3_15_1.md) - - [3.15.2 String](./chapter_3/chapter_3_15_2.md) - - [3.15.3 HashMap](./chapter_3/chapter_3_15_3.md) - - [3.15.4 HashSet](./chapter_3/chapter_3_15_4.md) - - [3.15.5 LinkedList](./chapter_3/chapter_3_15_5.md) - - [3.15.6 BTreeMap](./chapter_3/chapter_3_15_6.md) - - [3.15.7 BTreeSet](./chapter_3/chapter_3_15_7.md) - - [3.20 Rust并发编程](./chapter_3/chapter_3_20.md) - - [3.21 unsafe编程](./chapter_3/chapter_3_21.md) + - [Trait](./chapter_3/chapter_3_10.md) + - [3.10.1 trait基础](./chapter_3/chapter_3_10_1.md) + - [3.10.2 trait对象-todo](./chapter_3/chapter_3_10_2.md) + - [3.10.3 常见的trait](./chapter_3/chapter_3_10_3.md) + - [生命周期-todo](./chapter_3/chapter_3_11.md) + - [错误处理-todo](./chapter_3/chapter_3_12.md) + - [闭包](./chapter_3/chapter_3_13.md) + - [迭代器](./chapter_3/chapter_3_14.md) + - [常见Collections](./chapter_3/chapter_3_15.md) + - [Vector](./chapter_3/chapter_3_15_1.md) + - [String](./chapter_3/chapter_3_15_2.md) + - [HashMap](./chapter_3/chapter_3_15_3.md) + - [HashSet](./chapter_3/chapter_3_15_4.md) + - [LinkedList](./chapter_3/chapter_3_15_5.md) + - [BTreeMap](./chapter_3/chapter_3_15_6.md) + - [BTreeSet](./chapter_3/chapter_3_15_7.md) + - [智能指针-todo](./chapter_3/chapter_3_16.md) + - [包、crate、模块-todo](./chapter_3/chapter_3_17.md) + - [测试-todo](./chapter_3/chapter_3_18.md) + - [再谈注释-todo](./chapter_3/chapter_3_19.md) + - [Rust并发编程](./chapter_3/chapter_3_20.md) + - [unsafe编程](./chapter_3/chapter_3_21.md) + - [FFI介绍-todo](./chapter_3/chapter_3_22.md) + - [宏介绍-todo](./chapter_3/chapter_3_23.md) - [Rust使用技巧]() - [Rust代码风格与格式化](./chapter_4/chapter_4_1.md) - [使用 Clippy 进行代码静态检查](./chapter_4/chapter_4_2.md) diff --git a/src/chapter_3/chapter_3_10.md b/src/chapter_3/chapter_3_10.md new file mode 100644 index 0000000..dd0ee51 --- /dev/null +++ b/src/chapter_3/chapter_3_10.md @@ -0,0 +1,5 @@ +# 3.10 Trait + +- [3.10.1 trait基础](./chapter_3_10_1.md) +- [3.10.2 trait对象](./chapter_3_10_2.md) +- [3.10.3 常见的trait](./chapter_3_10_3.md) diff --git a/src/chapter_3/chapter_3_10_1.md b/src/chapter_3/chapter_3_10_1.md new file mode 100644 index 0000000..4d789df --- /dev/null +++ b/src/chapter_3/chapter_3_10_1.md @@ -0,0 +1,509 @@ +# 3.10.1 trait基础 + +trait定义了一组可以被共享的行为,只要实现了trait,就可以在代码中使用这组行为,它类似于其它语言中的接口(interface): +- 可以通过trait以抽象的方式定义共享的行为; +- 可以使用trait bounds指定泛型是任何拥有特定行为的类型。 + +## 1. 定义trait + +定义trait就是定义一组行为,如下: + +```rust +pub trait GetInformation { + fn get_name(&self) -> &String; + fn get_age(&self) -> u32; +} +``` + +上述代码定义了一个叫做`GetInfomation`的trait,该trait提供了`get_name`和`get_age`方法。 + +## 2. 为类型实现trait + +- 为类型实现trait + + 代码示例如下: + ```rust + // 定义trait + pub trait GetInformation { + fn get_name(&self) -> &String; + fn get_age(&self) -> u32; + } + + pub struct Student { + pub name: String, + pub age: u32, + } + // 为Student类型实现GetInformation trait + impl GetInformation for Student { + fn get_name(&self) -> &String { + &self.name + } + fn get_age(&self) -> u32 { + self.age + } + } + + pub struct Teacher { + pub name: String, + pub age: u32, + } + // 为Teacher类型实现GetInformation trait + impl GetInformation for Teacher { + fn get_name(&self) -> &String { + &self.name + } + fn get_age(&self) -> u32 { + self.age + } + } + + fn main() { + let s = Student { + name: "alice".to_string(), + age: 18, + }; + // 可以在类型Student上使用GetInfomation trait中定义的方法 + println!("s.name = {:?}, s.age = {:?}", s.get_name(), s.get_age()); + + let t = Teacher { + name: "bob".to_string(), + age: 25, + }; + // 可以类型Teacher使用GetInfomation trait中定义的方法 + println!("t.name = {:?}, t.age = {:?}", t.get_name(), t.get_age()); + } + ``` + +- 可以在trait定义时提供默认实现 + + 可以在定义trait的时候提供默认的行为,trait的类型可以使用默认的行为,示例如下: + ```rust + // 定义trait + pub trait GetInformation { + fn get_name(&self) -> &String; + fn get_age(&self) -> u32 { // 在定义trait时就提供默认实现 + 25u32 + } + } + + pub struct Student { + pub name: String, + pub age: u32, + } + // 为Student类型实现GetInformation trait + impl GetInformation for Student { + fn get_name(&self) -> &String { + &self.name + } + // 实现get_age方法,Student不会使用trait定义时的默认实现 + fn get_age(&self) -> u32 { + self.age + } + } + + pub struct Teacher { + pub name: String, + pub age: u32, + } + // 为Teacher类型实现GetInformation trait + impl GetInformation for Teacher { + fn get_name(&self) -> &String { + &self.name + } + // 不实现get_age方法,将使用trait的默认实现 + } + + fn main() { + let s = Student { + name: "alice".to_string(), + age: 18, + }; + // 可以在类型Student上使用GetInfomation trait中定义的方法 + // 输出t.name = "bob", t.age = 25 + println!("s.name = {:?}, s.age = {:?}", s.get_name(), s.get_age()); + + let t = Teacher { + name: "bob".to_string(), + age: 25, + }; + // 可以类型Teacher使用GetInfomation trait中定义的方法(t.get_age将使用定义trait时的默认方法) + // 输出t.name = "bob", t.age = 25 + println!("t.name = {:?}, t.age = {:?}", t.get_name(), t.get_age()); + } + ``` + +如果定义trait时提供了某个方法的默认实现,则: + - 如果为类型实现该trait时,为该类型实现了此方法,则使用自己实现的方法(如上面示例中的Student类型); + - 如果为类型实现该trait时,没有为该类型实现此方法,则该类型使用trait提供的默认实现(如上面的Teacher类型)。 + +## 3. trait作为参数 + +- trait作为参数 + trait可以用来参数,示例如下: + + ```rust + // 定义trait + pub trait GetInformation { + fn get_name(&self) -> &String; + fn get_age(&self) -> u32 { + 25u32 + } + } + + pub struct Student { + pub name: String, + pub age: u32, + } + // 为Student类型实现GetInformation trait + impl GetInformation for Student { + fn get_name(&self) -> &String { + &self.name + } + fn get_age(&self) -> u32 { + self.age + } + } + + pub struct Teacher { + pub name: String, + pub age: u32, + } + // 为Teacher类型实现GetInformation trait + impl GetInformation for Teacher { + fn get_name(&self) -> &String { + &self.name + } + } + + // 参数类型必须是实现了GetInfomation trait的类型 + pub fn print_information(item: impl GetInformation) { + println!("name = {}", item.get_name()); + println!("age = {}", item.get_age()); + } + + fn main() { + let s = Student { + name: "alice".to_string(), + age: 18, + }; + print_information(s); + + let t = Teacher { + name: "bob".to_string(), + age: 25, + }; + print_information(t); + } + ``` + + 在上面的例子中,函数`pub fn print_information(item: impl GetInformation)`的要求参数item必须实现`GetInformation trait`,否则无法调用该参数。 + +- 使用`trait bound`语法 + + 上面中的print_information函数还可以写成如下: + ```rust + // 使用trait bound的写法一 + pub fn print_information(item: T) { + println!("name = {}", item.get_name()); + println!("age = {}", item.get_age()); + } + ``` + + 这种写法叫做Trait bound语法,它是Rust中用于指定泛型类型参数所需的trait的一种方式,它还可以使用where关键字写成如下: + ```rust + // 使用trait bound的写法二 + pub fn print_information(item: T) + where + T: GetInformation, + { + println!("name = {}", item.get_name()); + println!("age = {}", item.get_age()); + } + ``` + +- 通过“`+`”指定多个trait bound + + 可以要求类型实现多个trait,示例如下: + ```rust + pub trait GetName { + fn get_name(&self) -> &String; + } + pub trait GetAge { + fn get_age(&self) -> u32; + } + + //使用trait bound写法一,类型T必须实现GetName和GetAge trait + pub fn print_information1(item: T) { + println!("name = {}", item.get_name()); + println!("age = {}", item.get_age()); + } + + //使用trait bound写法二,类型T必须实现GetName和GetAge trait + pub fn print_information2(item: T) + where + T: GetName + GetAge, + { + println!("name = {}", item.get_name()); + println!("age = {}", item.get_age()); + } + + #[derive(Clone)] + struct Student { + name: String, + age: u32, + } + + impl GetName for Student { + fn get_name(&self) -> &String { + &self.name + } + } + + impl GetAge for Student { + fn get_age(&self) -> u32 { + self.age + } + } + + fn main() { + let s = Student { + name: "alice".to_string(), + age: 18u32, + }; + print_information1(s.clone()); + print_information1(s); + } + ``` + + 在上面的代码中,`print_information1`和`print_information2`函数要求其参数类型T必须实现`GetName`和`GetAge`两个trait,通过`+`来进行多个约束的连接。 + +## 4. 返回trait的类型 + +trait类型可以作为函数的返回类型,示例如下: +```rust +pub trait GetName { + fn get_name(&self) -> &String; +} + +struct Student { + name: String, +} + +impl GetName for Student { + fn get_name(&self) -> &String { + &self.name + } +} + +// trait类型作为返回的参数 +pub fn produce_item_with_name() -> impl GetName { + // 返回一个实现了GetName trait的类型 + Student { + name: "alice".to_string(), + } +} + +fn main() { + let s = produce_item_with_name(); + println!("name: {:?}", s.get_name()); +} +``` + +上面代码中,`produce_item_with_name`函数返回了一个实现了`GetName trait`的类型。不过需要注意的是,这种方式返回的是单一类型,例如如下的代码就是错误的,无法编译通过: +```rust +pub trait GetName { + fn get_name(&self) -> &String; +} + +struct Student { + name: String, +} + +impl GetName for Student { + fn get_name(&self) -> &String { + &self.name + } +} +struct Teacher { + name: String, +} + +impl GetName for Teacher { + fn get_name(&self) -> &String { + &self.name + } +} + +// 下面的代码将是错误的,无法编译通过,因为在编译时,返回的类型就确定为某一个实现了GetName trait的具体类型了 +pub fn produce_item_with_name(is_teacher: bool) -> impl GetName { + let result = if is_teacher { + Teacher { + name: "alice".to_string(), + } + } else { + Student { + name: "alice".to_string(), + } + }; + + result +} + +fn main() { + let s = produce_item_with_name(false); + println!("name: {:?}", s.get_name()); +} +``` + +错误原因分析(非常重要): +上面的代码中的`produce_item_with_name`函数的定义实际上等价于如下: +```rust +pub fn produce_item_with_name(is_teacher: bool) -> T { + ... + + result +} +``` + +返回的值相当于是一个泛型,这个泛型要求要实现`GetName`这个trait。回顾泛型的知识,Rust实际上是在编译的时候把泛型换成了具体的类型,所以上面的定义中,T在编译时会变成确定的某个类型。所以在编译时,上面的代码可能被翻译成如下两种情况: + +```rust +// 编译时代码将被翻译成如下: +pub fn produce_item_with_name(is_teacher: bool) -> Student { + let result = if is_teacher { + Teacher { name: "alice".to_string() } + } else { + Student { name: "alice".to_string() } + }; + + result +} + +// 也可能翻译成如下: +pub fn produce_item_with_name(is_teacher: bool) -> Student { + let result = if is_teacher { + Teacher { name: "alice".to_string() } + } else { + Student { name: "alice".to_string() } + }; + + result +} +``` + +无论是哪种情况,都是错误的。 + +那如果需要返回多种实现了trait的类型,则需要使用后续讲解的内容trait对象(3.9.2节)来满足需求。 + +## 5. 使用`trait bound`有条件的实现方法 + +通过使用带有 `trait bound `的泛型参数的`impl` 块,可以有条件地只为那些实现了特定 trait 的类型实现方法,示例如下: + +```rust + +pub trait GetName { + fn get_name(&self) -> &String; +} +pub trait GetAge { + fn get_age(&self) -> u32; +} +struct PeopleMatchInformation { + master: T, + employee: U, +} +// 11-15行也可以写成: impl PeopleMatchInformation +impl PeopleMatchInformation +where + T: GetName + GetAge, // T和U都必须实现GetName和GetAge trait + U: GetName + GetAge, +{ + fn print_all_information(&self) { + println!("teacher name = {}", self.master.get_name()); + println!("teacher age = {}", self.master.get_age()); + println!("student name = {}", self.employee.get_name()); + println!("student age = {}", self.employee.get_age()); + } +} +//使用 +pub struct Teacher { + pub name: String, + pub age: u32, +} +impl GetName for Teacher { + fn get_name(&self) -> &String { + &(self.name) + } +} +impl GetAge for Teacher { + fn get_age(&self) -> u32 { + self.age + } +} +pub struct Student { + pub name: String, + pub age: u32, +} +impl GetName for Student { + fn get_name(&self) -> &String { + &(self.name) + } +} +impl GetAge for Student { + fn get_age(&self) -> u32 { + self.age + } +} +fn main() { + let t = Teacher { + name: String::from("andy"), + age: 32, + }; + let s = Student { + name: String::from("harden"), + age: 47, + }; + let m = PeopleMatchInformation { + master: t, + employee: s, + }; + m.print_all_information(); +} +``` + +上面的代码中,就是为`PeopleMatchInformation`有条件的实现`print_all_information`方法。 + +## 6. 对任何实现了特定trait的类型有条件的实现trait + +在Rust中,另外一种比较常见的trait的用法就是对实现了特定trait的类型有条件的实现trait,示例如下: + +```rust +// trait定义 +pub trait GetName { + fn get_name(&self) -> &String; +} +pub trait PrintName { + fn print_name(&self); +} +// 为实现了GetName trait的类型实现PrintName trait +impl PrintName for T { + fn print_name(&self) { + println!("name = {}", self.get_name()); + } +} +// 将为Student实现对应的trait +pub struct Student { + pub name: String, +} +impl GetName for Student { + fn get_name(&self) -> &String { + &(self.name) + } +} +fn main() { + let s = Student { + name: String::from("Andy"), + }; + s.print_name(); //student实现了GetName trait,因此可是直接使用PrintName trait中的函数print_name +} +``` +上面的例子中,就是为实现了`GetName trait`的类型实现`PrintName trait`。 diff --git a/src/chapter_3/chapter_3_10_2.md b/src/chapter_3/chapter_3_10_2.md new file mode 100644 index 0000000..25d3108 --- /dev/null +++ b/src/chapter_3/chapter_3_10_2.md @@ -0,0 +1,3 @@ +# 3.10.2 trait对象 + +todo diff --git a/src/chapter_3/chapter_3_10_3.md b/src/chapter_3/chapter_3_10_3.md new file mode 100644 index 0000000..268ef16 --- /dev/null +++ b/src/chapter_3/chapter_3_10_3.md @@ -0,0 +1,113 @@ +# 3.10.3 常见的trait + +Rust 常见的 trait 包括: +- `std::fmt::Display`: 格式化打印用户友好字符串。 +- `std::fmt::Debug`: 格式化打印调试字符串。 +- `std::cmp::PartialEq`: 比较值相等。 +- `std::cmp::PartialOrd`: 比较值顺序。 +- `std::cmp::Eq`: 类型完全相等关系。 +- `std::cmp::Ord`: 类型完全顺序关系。 +- `std::clone::Clone`: 创建类型副本。 +- `std::ops::Add`: 定义加法操作。 +- `std::ops::Mul`: 定义乘法操作。 +- `std::iter::Iterator`: 实现迭代器。 + +下面分别介绍: + +## 1. std::fmt::Display: + +```rust +use std::fmt; + +struct Person { + name: String, + age: u32, +} + +impl fmt::Display for Person { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} ({} years)", self.name, self.age) + } +} +``` + +## 2. std::fmt::Debug: + +```rust +#[derive(Debug)] +struct Person { + name: String, + age: u32, +} +``` + +## 3. std::cmp::PartialEq 和 std::cmp::Eq: +```rust +#[derive(PartialEq, Eq)] +struct Point { + x: i32, + y: i32, +} +``` + +## 4. std::cmp::PartialOrd 和 std::cmp::Ord: + +```rust +#[derive(PartialOrd, Ord)] +struct Point { + x: i32, + y: i32, +} +``` + +## 5. std::clone::Clone: + +```rust +#[derive(Clone)] +struct Point { + x: i32, + y: i32, +} +``` + +## 6. std::ops::Add: + +```rust +use std::ops::Add; + +struct Point { + x: i32, + y: i32, +} + +impl Add for Point { + type Output = Point; + + fn add(self, other: Point) -> Point { + Point { + x: self.x + other.x, + y: self.y + other.y, + } + } +} +``` + +## 7. std::iter::Iterator: +```rust +struct Counter { + count: u32, +} + +impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + self.count += 1; + if self.count < 6 { + Some(self.count) + } else { + None + } + } +} +``` diff --git a/src/chapter_3/chapter_3_11.md b/src/chapter_3/chapter_3_11.md new file mode 100644 index 0000000..b81b209 --- /dev/null +++ b/src/chapter_3/chapter_3_11.md @@ -0,0 +1,3 @@ +# 生命周期 + +todo diff --git a/src/chapter_3/chapter_3_12.md b/src/chapter_3/chapter_3_12.md new file mode 100644 index 0000000..2cf535b --- /dev/null +++ b/src/chapter_3/chapter_3_12.md @@ -0,0 +1,3 @@ +# 错误处理 + +todo diff --git a/src/chapter_3/chapter_3_16.md b/src/chapter_3/chapter_3_16.md new file mode 100644 index 0000000..a36c307 --- /dev/null +++ b/src/chapter_3/chapter_3_16.md @@ -0,0 +1,3 @@ +# 智能指针 + +todo diff --git a/src/chapter_3/chapter_3_17.md b/src/chapter_3/chapter_3_17.md new file mode 100644 index 0000000..ab48e9c --- /dev/null +++ b/src/chapter_3/chapter_3_17.md @@ -0,0 +1,3 @@ +# 包、crate、模块 + +todo diff --git a/src/chapter_3/chapter_3_18.md b/src/chapter_3/chapter_3_18.md new file mode 100644 index 0000000..de9284c --- /dev/null +++ b/src/chapter_3/chapter_3_18.md @@ -0,0 +1,3 @@ +# 测试 + +todo diff --git a/src/chapter_3/chapter_3_19.md b/src/chapter_3/chapter_3_19.md new file mode 100644 index 0000000..5005b28 --- /dev/null +++ b/src/chapter_3/chapter_3_19.md @@ -0,0 +1,3 @@ +# 再谈注释 + +todo diff --git a/src/chapter_3/chapter_3_22.md b/src/chapter_3/chapter_3_22.md new file mode 100644 index 0000000..7b31e99 --- /dev/null +++ b/src/chapter_3/chapter_3_22.md @@ -0,0 +1,3 @@ +# FFI介绍 + +todo diff --git a/src/chapter_3/chapter_3_23.md b/src/chapter_3/chapter_3_23.md new file mode 100644 index 0000000..3f1134c --- /dev/null +++ b/src/chapter_3/chapter_3_23.md @@ -0,0 +1,3 @@ +# 宏介绍 + +todo diff --git a/src/chapter_3/chapter_3_9.md b/src/chapter_3/chapter_3_9.md index 7f0a076..0fce6fc 100644 --- a/src/chapter_3/chapter_3_9.md +++ b/src/chapter_3/chapter_3_9.md @@ -1,6 +1,5 @@ # 3.9 泛型 - 泛型是具体类型或者其它属性的抽象代替,用于减少代码的重复。在编写Rust代码时,可以用泛型来表示各种各样的数据类型,等到编译阶段,泛型则被替换成它所代表的的具体的数据类型。 ## 3.9.1 函数定义中的泛型