Update trait.md

pull/198/head
Jesse 3 years ago committed by GitHub
parent 94f1778ebe
commit d5476ba954
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,9 +1,9 @@
# 特征Trait # 特征Trait
如果我们想定义一个文件系统,那么把这个文件系统跟底层存储解耦是很重要的。文件操作主要包含三个:`open`、`write`、`read`,这些操作可以发生在硬盘,也可以发生在缓存,可以通过网络也可以通过(我实在编不下去了,大家来帮帮我)。总之如果你要为每一种情况都单独实现一套代码,那这种实现将过于繁杂,而且也没那个必要。 如果我们想定义一个文件系统,那么把系统跟底层存储解耦是很重要的。文件操作主要包含三个:`open`、`write`、`read`,这些操作可以发生在硬盘,也可以发生在缓存,可以通过网络也可以通过(我实在编不下去了,大家来帮帮我)。总之如果你要为每一种情况都单独实现一套代码,那这种实现将过于繁杂,而且也没那个必要。
要解决上述问题需要把这些行为抽象出来就要使用Rust中的特征 `trait` 概念。可能你是第一次听说这个名词,但是不要怕,如果学过其他语言,那么大概率你听说过接口,没错,特征很类似接口。 要解决上述问题需要把这些行为抽象出来就要使用Rust中的特征 `trait` 概念。可能你是第一次听说这个名词,但是不要怕,如果学过其他语言,那么大概率你听说过接口,没错,特征很类似接口。
在之前的代码中,我们也多次见过特征的使用,例如 `#[derive(Debug)]`它在我们定义的类型struct上自动派生 `Debug` 特征,接着可以使用 `println!("{:?}",x)` 打印我们定义的类型;再例如: 在之前的代码中,我们也多次见过特征的使用,例如 `#[derive(Debug)]`它在我们定义的类型struct上自动派生 `Debug` 特征,接着可以使用 `println!("{:?}",x)` 打印这个类型;再例如:
```rust ```rust
fn add<T: std::ops::Add<Output = T>>(a:T, b:T) -> T { fn add<T: std::ops::Add<Output = T>>(a:T, b:T) -> T {
a + b a + b
@ -23,9 +23,9 @@ pub trait Summary {
} }
``` ```
这里使用 `trait` 关键字来声明一个特征,`Summary` 是特征名。在大括号中定义描述该特征包含的所有方法,比如这个特性中包含方法 `fn summarize(&self) -> String` 这里使用 `trait` 关键字来声明一个特征,`Summary` 是特征名。在大括号中定义了该特征的所有方法,在这个例子中是 `fn summarize(&self) -> String`
特征只定义行为看起来是什么样的,而不定义行为具体是怎么样的。因此,我们只定义特征方法的签名,而不进行实现,此方法签名结尾是 `;`,而不是一个 `{}` 特征只定义行为看起来是什么样的,而不定义行为具体是怎么样的。因此,我们只定义特征方法的签名,而不进行实现,此方法签名结尾是 `;`,而不是一个 `{}`
接下来,每一个实现这个特征的类型都需要具体实现该特征的相应方法,编译器也会确保任何实现 `Summary` 特征的类型都拥有与这个签名的定义完全一致的 `summarize` 方法。 接下来,每一个实现这个特征的类型都需要具体实现该特征的相应方法,编译器也会确保任何实现 `Summary` 特征的类型都拥有与这个签名的定义完全一致的 `summarize` 方法。
@ -80,14 +80,14 @@ fn main() {
sunface发表了微博好像微博没Tweet好用 sunface发表了微博好像微博没Tweet好用
``` ```
说实话,如果特征仅仅如此,你可能会觉得花里胡哨没啥用,接下就让你见识下 `trait` 真正的威力。 说实话,如果特征仅仅如此,你可能会觉得花里胡哨没啥用,接下就让你见识下 `trait` 真正的威力。
#### 特征定义与实现的位置(孤儿规则) #### 特征定义与实现的位置(孤儿规则)
上面我们将 `Summary` 定义成了 `pub` 公开的。这样,如果他人想要使用我们的 `Summary` 特征,则可以引入到他们的包中,然后再进行实现。 上面我们将 `Summary` 定义成了 `pub` 公开的。这样,如果他人想要使用我们的 `Summary` 特征,则可以引入到他们的包中,然后再进行实现。
关于特征实现与定义的位置,有一条非常重要的原则:**如果你想要为类型 `A` 实现特征 `T`,那么 `A` 或者 `T `至少有一个是在当前作用域中定义的!**。例如我们可以为上面的 `Post` 类型实现标准库中的 `Display` 特征,这是因为 `Post` 类型定义在当前的作用域中。同时,我们也可以在当前包中为 `String` 类型实现 `Summary` 特征,因为 `Summary` 定义在当前作用域中。 关于特征实现与定义的位置,有一条非常重要的原则:**如果你想要为类型 `A` 实现特征 `T`,那么 `A` 或者 `T `至少有一个是在当前作用域中定义的!**。例如我们可以为上面的 `Post` 类型实现标准库中的 `Display` 特征,这是因为 `Post` 类型定义在当前的作用域中。同时,我们也可以在当前包中为 `String` 类型实现 `Summary` 特征,因为 `Summary` 定义在当前作用域中。
但是你无法在当前作用域中,为 `String` 类型实现 `Display` 特征,因为它们俩都定义在标准库中,它俩的定义所在的位置都不在当前作用域,跟你半毛钱关系都没有,看看就行了。 但是你无法在当前作用域中,为 `String` 类型实现 `Display` 特征,因为它们俩都定义在标准库中,定义所在的位置都不在当前作用域,跟你半毛钱关系都没有,看看就行了。
该规则被称为**孤儿规则**,可以确保其它人编写的代码不会破坏你的代码,也确保了你不会莫名其妙就破坏了风马牛不相及的代码。 该规则被称为**孤儿规则**,可以确保其它人编写的代码不会破坏你的代码,也确保了你不会莫名其妙就破坏了风马牛不相及的代码。

Loading…
Cancel
Save