2022-03-08 03:15:53 -06:00
|
|
|
|
# 进一步深入特征
|
2022-03-08 02:30:50 -06:00
|
|
|
|
|
2022-03-08 03:15:53 -06:00
|
|
|
|
## 关联类型
|
|
|
|
|
关联类型主要用于提升代码的可读性,例如以下代码 :
|
2022-03-08 02:30:50 -06:00
|
|
|
|
```rust
|
|
|
|
|
pub trait CacheableItem: Clone + Default + fmt::Debug + Decodable + Encodable {
|
|
|
|
|
type Address: AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash;
|
|
|
|
|
fn is_null(&self) -> bool;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-08 03:15:53 -06:00
|
|
|
|
相比 `AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash`, `Address` 的使用可以极大的极少其它类型在实现该特征时所需的模版代码.
|
2022-03-08 02:30:50 -06:00
|
|
|
|
|
|
|
|
|
1. 🌟🌟🌟
|
|
|
|
|
```rust,editable
|
|
|
|
|
|
|
|
|
|
struct Container(i32, i32);
|
|
|
|
|
|
2022-03-08 03:15:53 -06:00
|
|
|
|
// 使用关联类型实现重新实现以下特征
|
|
|
|
|
// trait Contains {
|
2022-03-08 02:30:50 -06:00
|
|
|
|
// type A;
|
|
|
|
|
// type B;
|
|
|
|
|
|
|
|
|
|
trait Contains<A, B> {
|
|
|
|
|
fn contains(&self, _: &A, _: &B) -> bool;
|
|
|
|
|
fn first(&self) -> i32;
|
|
|
|
|
fn last(&self) -> 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)
|
|
|
|
|
}
|
|
|
|
|
// Grab the first number.
|
|
|
|
|
fn first(&self) -> i32 { self.0 }
|
|
|
|
|
|
|
|
|
|
// Grab the last number.
|
|
|
|
|
fn last(&self) -> i32 { self.1 }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn difference<A, B, C: Contains<A, B>>(container: &C) -> i32 {
|
|
|
|
|
container.last() - container.first()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-08 03:18:56 -06:00
|
|
|
|
## 定义默认的泛型类型参数
|
|
|
|
|
当我们使用泛型类型参数时,可以为该泛型参数指定一个具体的默认类型,这样当实现该特征时,如果该默认类型可以使用,那用户再无需手动指定具体的类型。
|
2022-03-08 02:30:50 -06:00
|
|
|
|
|
2022-03-08 07:52:35 -06:00
|
|
|
|
2. 🌟🌟
|
2022-03-08 02:30:50 -06:00
|
|
|
|
```rust,editable
|
|
|
|
|
|
|
|
|
|
use std::ops::Sub;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
struct Point<T> {
|
|
|
|
|
x: T,
|
|
|
|
|
y: T,
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-08 03:18:56 -06:00
|
|
|
|
// 用三种方法填空: 其中两种使用默认的泛型参数,另外一种不使用
|
2022-03-08 02:30:50 -06:00
|
|
|
|
impl __ {
|
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
|
|
fn sub(self, other: Self) -> Self::Output {
|
|
|
|
|
Point {
|
|
|
|
|
x: self.x - other.x,
|
|
|
|
|
y: self.y - other.y,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 },
|
|
|
|
|
Point { x: 1, y: 3 });
|
|
|
|
|
|
|
|
|
|
println!("Success!")
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-08 03:18:56 -06:00
|
|
|
|
## 完全限定语法
|
2022-03-08 03:28:49 -06:00
|
|
|
|
在 Rust 中,两个不同特征的方法完全可以同名,且你可以为同一个类型同时实现这两个特征。这种情况下,就出现了一个问题:该如何调用这两个特征上定义的同名方法。为了解决这个问题,我们需要使用完全限定语法( Fully Qualified Syntax )。
|
2022-03-08 02:30:50 -06:00
|
|
|
|
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
#### 示例
|
2022-03-08 02:30:50 -06:00
|
|
|
|
```rust,editable
|
|
|
|
|
trait UsernameWidget {
|
|
|
|
|
fn get(&self) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-15 19:56:46 -06:00
|
|
|
|
trait AgeWidget {
|
2022-03-08 02:30:50 -06:00
|
|
|
|
fn get(&self) -> u8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Form {
|
|
|
|
|
username: String,
|
|
|
|
|
age: u8,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl UsernameWidget for Form {
|
|
|
|
|
fn get(&self) -> String {
|
|
|
|
|
self.username.clone()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AgeWidget for Form {
|
|
|
|
|
fn get(&self) -> u8 {
|
|
|
|
|
self.age
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let form = Form{
|
|
|
|
|
username: "rustacean".to_owned(),
|
|
|
|
|
age: 28,
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
// 如果你反注释下面一行代码,将看到一个错误: Fully Qualified Syntax
|
|
|
|
|
// 毕竟,这里有好几个同名的 `get` 方法
|
|
|
|
|
//
|
2022-03-08 02:30:50 -06:00
|
|
|
|
// println!("{}", form.get());
|
|
|
|
|
|
|
|
|
|
let username = UsernameWidget::get(&form);
|
|
|
|
|
assert_eq!("rustacean".to_owned(), username);
|
2022-03-08 03:28:49 -06:00
|
|
|
|
let age = AgeWidget::get(&form); // 你还可以使用以下语法 `<Form as AgeWidget>::get`
|
2022-03-08 02:30:50 -06:00
|
|
|
|
assert_eq!(28, age);
|
|
|
|
|
|
|
|
|
|
println!("Success!")
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
#### 练习题
|
2022-03-08 02:30:50 -06:00
|
|
|
|
3. 🌟🌟
|
|
|
|
|
```rust,editable
|
|
|
|
|
trait Pilot {
|
|
|
|
|
fn fly(&self) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trait Wizard {
|
|
|
|
|
fn fly(&self) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Human;
|
|
|
|
|
|
|
|
|
|
impl Pilot for Human {
|
|
|
|
|
fn fly(&self) -> String {
|
|
|
|
|
String::from("This is your captain speaking.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Wizard for Human {
|
|
|
|
|
fn fly(&self) -> String {
|
|
|
|
|
String::from("Up!")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Human {
|
|
|
|
|
fn fly(&self) -> String {
|
|
|
|
|
String::from("*waving arms furiously*")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let person = Human;
|
|
|
|
|
|
|
|
|
|
assert_eq!(__, "This is your captain speaking.");
|
|
|
|
|
assert_eq!(__, "Up!");
|
|
|
|
|
|
|
|
|
|
assert_eq!(__, "*waving arms furiously*");
|
|
|
|
|
|
|
|
|
|
println!("Success!")
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Supertraits
|
2022-03-08 03:28:49 -06:00
|
|
|
|
有些时候我们希望在特征上实现类似继承的特性,例如让一个特征 `A` 使用另一个特征 `B` 的功能。这种情况下,一个类型要实现特征 `A` 首先要实现特征 `B`, 特征 `B` 就被称为 `supertrait`
|
2022-03-08 02:30:50 -06:00
|
|
|
|
|
2022-03-08 07:52:35 -06:00
|
|
|
|
4. 🌟🌟🌟
|
2022-03-08 02:30:50 -06:00
|
|
|
|
```rust,editable
|
|
|
|
|
|
|
|
|
|
trait Person {
|
|
|
|
|
fn name(&self) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
// Person 是 Student 的 supertrait .
|
|
|
|
|
// 实现 Student 需要同时实现 Person.
|
2022-03-08 02:30:50 -06:00
|
|
|
|
trait Student: Person {
|
|
|
|
|
fn university(&self) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trait Programmer {
|
|
|
|
|
fn fav_language(&self) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
// CompSciStudent (computer science student) 是 Programmer
|
|
|
|
|
// 和 Student 的 subtrait. 实现 CompSciStudent 需要先实现这两个 supertraits.
|
2022-03-08 02:30:50 -06:00
|
|
|
|
trait CompSciStudent: Programmer + Student {
|
|
|
|
|
fn git_username(&self) -> String;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
|
|
|
|
|
format!(
|
|
|
|
|
"My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
|
|
|
|
|
student.name(),
|
|
|
|
|
student.university(),
|
|
|
|
|
student.fav_language(),
|
|
|
|
|
student.git_username()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct CSStudent {
|
|
|
|
|
name: String,
|
|
|
|
|
university: String,
|
|
|
|
|
fav_language: String,
|
|
|
|
|
git_username: String
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
// 为 CSStudent 实现所需的特征
|
2022-03-08 02:30:50 -06:00
|
|
|
|
impl ...
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let student = CSStudent {
|
|
|
|
|
name: "Sunfei".to_string(),
|
|
|
|
|
university: "XXX".to_string(),
|
|
|
|
|
fav_language: "Rust".to_string(),
|
|
|
|
|
git_username: "sunface".to_string()
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
// 填空
|
2022-03-08 02:30:50 -06:00
|
|
|
|
println!("{}", comp_sci_student_greeting(__));
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
## 孤儿原则
|
|
|
|
|
关于孤儿原则的详细介绍请参见[特征定义与实现的位置孤儿规则](https://course.rs/basic/trait/trait#特征定义与实现的位置孤儿规则) 和 [在外部类型上实现外部特征](https://course.rs/basic/trait/advance-trait.html#在外部类型上实现外部特征newtype)。
|
2022-03-08 02:30:50 -06:00
|
|
|
|
|
|
|
|
|
|
2022-03-08 07:52:35 -06:00
|
|
|
|
5. 🌟🌟
|
2022-03-11 21:47:15 -06:00
|
|
|
|
```rust,editable
|
2022-03-08 02:30:50 -06:00
|
|
|
|
use std::fmt;
|
|
|
|
|
|
2022-03-08 03:28:49 -06:00
|
|
|
|
// 定义一个 newtype `Pretty`
|
2022-03-08 02:30:50 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for Pretty {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "\"{}\"", self.0.clone() + ", world")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let w = Pretty("hello".to_string());
|
|
|
|
|
println!("w = {}", w);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it :)
|