rust-by-practice/zh-CN/src/generics-traits/advanced-traits.md

278 lines
7.0 KiB
Markdown
Raw Normal View History

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 03:18:56 -06:00
1. 🌟🌟
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
## 完全限定语法
Nothing in Rust prevents a trait from having a method with the same name as another traits method, nor does Rust prevent you from implementing both traits on one type. Its also possible to implement a method directly on the type with the same name as methods from traits.
2022-03-08 02:30:50 -06:00
When calling methods with the same name, we have to use Fully Qualified Syntax.
#### Example
```rust,editable
trait UsernameWidget {
// Get the selected username out of this widget
fn get(&self) -> String;
}
trait AgeWidget {
// Get the selected age out of this widget
fn get(&self) -> u8;
}
// A form with both a UsernameWidget and an AgeWidget
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,
};
// If you uncomment this line, you'll get an error saying
// "multiple `get` found". Because, after all, there are multiple methods
// named `get`.
// println!("{}", form.get());
let username = UsernameWidget::get(&form);
assert_eq!("rustacean".to_owned(), username);
let age = AgeWidget::get(&form); // you can also use `<Form as AgeWidget>::get`
assert_eq!(28, age);
println!("Success!")
}
```
#### Exercise
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
Sometimes, you might need one trait to use another traits functionality( like the "inheritance" in other languages ). In this case, you need to rely on the dependent trait also being implemented. The trait you rely on is a `supertrait` of the trait youre implementing.
4. 🌟🌟🌟
```rust,editable
trait Person {
fn name(&self) -> String;
}
// Person is a supertrait of Student.
// Implementing Student requires you to also impl Person.
trait Student: Person {
fn university(&self) -> String;
}
trait Programmer {
fn fav_language(&self) -> String;
}
// CompSciStudent (computer science student) is a subtrait of both Programmer
// and Student. Implementing CompSciStudent requires you to impl both supertraits.
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
}
// IMPLEMENT the necessary traits for CSStudent to make the code work
impl ...
fn main() {
let student = CSStudent {
name: "Sunfei".to_string(),
university: "XXX".to_string(),
fav_language: "Rust".to_string(),
git_username: "sunface".to_string()
};
// FILL in the blank
println!("{}", comp_sci_student_greeting(__));
}
```
## Orphan Rules
We cant implement external traits on external types. For example, we cant implement the `Display` trait on `Vec<T>` within our own crate, because `Display` and `Vec<T>` are defined in the standard library and arent local to our crate.
This restriction is often called as the orphan rule, so named because the parent type is not present. This rule ensures that other peoples code cant break your code and vice versa.
Its possible to get around this restriction using the newtype pattern, which involves creating a new type in a tuple struct.
5. 🌟🌟
```rust
use std::fmt;
// DEFINE a newtype `Pretty` here
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 :)