This commit is contained in:
Davirain
2023-05-17 21:27:17 +08:00
parent 0cca915101
commit 2cabfff8b9
14 changed files with 678 additions and 13 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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<T: GetInformation>(item: T) {
println!("name = {}", item.get_name());
println!("age = {}", item.get_age());
}
```
这种写法叫做Trait bound语法它是Rust中用于指定泛型类型参数所需的trait的一种方式它还可以使用where关键字写成如下
```rust
// 使用trait bound的写法二
pub fn print_information<T>(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<T: GetName + GetAge>(item: T) {
println!("name = {}", item.get_name());
println!("age = {}", item.get_age());
}
//使用trait bound写法二类型T必须实现GetName和GetAge trait
pub fn print_information2<T>(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<T: GetName>(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<T, U> {
master: T,
employee: U,
}
// 11-15行也可以写成 impl<T: GetName + GetAge, U: GetName + GetAge> PeopleMatchInformation<T, U>
impl<T, U> PeopleMatchInformation<T, U>
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<T: GetName> 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`。

View File

@@ -0,0 +1,3 @@
# 3.10.2 trait对象
todo

View File

@@ -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::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
```

View File

@@ -0,0 +1,3 @@
# 生命周期
todo

View File

@@ -0,0 +1,3 @@
# 错误处理
todo

View File

@@ -0,0 +1,3 @@
# 智能指针
todo

View File

@@ -0,0 +1,3 @@
# 包、crate、模块
todo

View File

@@ -0,0 +1,3 @@
# 测试
todo

View File

@@ -0,0 +1,3 @@
# 再谈注释
todo

View File

@@ -0,0 +1,3 @@
# FFI介绍
todo

View File

@@ -0,0 +1,3 @@
# 宏介绍
todo

View File

@@ -1,6 +1,5 @@
# 3.9 泛型
泛型是具体类型或者其它属性的抽象代替用于减少代码的重复。在编写Rust代码时可以用泛型来表示各种各样的数据类型等到编译阶段泛型则被替换成它所代表的的具体的数据类型。
## 3.9.1 函数定义中的泛型