@ -3,7 +3,7 @@
要解决上述问题,需要把这些行为抽象出来,就要使用 Rust 中的特征 `trait` 概念。可能你是第一次听说这个名词,但是不要怕,如果学过其他语言,那么大概率你听说过接口,没错,特征很类似接口。
在之前的代码中,我们也多次见过特征的使用,例如 `#[derive(Debug)]` , 它在我们定义的类型( struct) 上自动派生 `Debug` 特征,接着可以使用 `println!("{:?}", x)` 打印这个类型;再例如:
在之前的代码中,我们也多次见过特征的使用,例如 `#[derive(Debug)]` ,它在我们定义的类型(` struct` )上自动派生 `Debug` 特征,接着可以使用 `println!("{:?}", x)` 打印这个类型;再例如:
```rust
fn add< T: std::ops::Add < Output = T > >(a:T, b:T) -> T {
a + b
@ -305,7 +305,7 @@ expected struct `Post`, found struct `Weibo`
报错提示我们 `if` 和 `else` 返回了不同的类型。如果想要实现返回不同的类型,需要使用下一章节中的[特征对象](./trait-object.md)。
## 修复上一节中的 `largest` 函数
还记得上一节中的[例子](./generic#泛型详解)吧, 当时留下一个疑问,该如何解决编译报错:
还记得上一节中的[例子](./generic#泛型详解)吧, 当时留下一个疑问,该如何解决编译报错:
```rust
error[E0369]: binary operation `>` cannot be applied to type `T` // 无法在 `T` 类型上应用`>`运算符
--> src/main.rs:5:17
@ -315,13 +315,13 @@ error[E0369]: binary operation `>` cannot be applied to type `T` // 无法在`T`
| |
| T
|
help: consider restricting type parameter `T` // 考虑使用以下的特征来约束T
help: consider restricting type parameter `T` // 考虑使用以下的特征来约束 ` T`
|
1 | fn largest< T: std::cmp::PartialOrd > (list: & [T]) -> T {
| ^^^^^^^^^^^^^^^^^^^^^^
```
在 `largest` 函数体中我们想要使用大于运算符(>)比较两个 `T` 类型的值。这个运算符是标准库中特征 `std::cmp::PartialOrd` 的一个默认方法。所以需要在 `T` 的特征约束中指定 `PartialOrd` ,这样 `largest` 函数可以用于内部元素类型可比较大小的数组切片。
在 `largest` 函数体中我们想要使用大于运算符(` >` )比较两个 `T` 类型的值。这个运算符是标准库中特征 `std::cmp::PartialOrd` 的一个默认方法。所以需要在 `T` 的特征约束中指定 `PartialOrd` ,这样 `largest` 函数可以用于内部元素类型可比较大小的数组切片。
由于 `PartialOrd` 位于 `prelude` 中所以并不需要通过 `std::cmp` 手动将其引入作用域。所以可以将 `largest` 的签名修改为如下:
```rust
@ -349,9 +349,9 @@ error[E0507]: cannot move out of borrowed content
| cannot move out of borrowed content
```
错误的核心是 `cannot move out of type [T], a non-copy slice` , 原因是`T`没有[实现`Copy`特性](../ownership/ownership.md#拷贝(浅拷贝)),因此我们只能把所有权进行转移,毕竟只有`i32`等基础类型才实现了 `Copy` 特性,可以存储在栈上,而 `T` 可以指代任何类型(严格来说是实现了`PartialOrd`特征的所有类型) 。
错误的核心是 `cannot move out of type [T], a non-copy slice` ,原因是 `T` 没有[实现 `Copy` 特性](../ownership/ownership.md#拷贝(浅拷贝)),因此我们只能把所有权进行转移,毕竟只有 `i32` 等基础类型才实现了 `Copy` 特性,可以存储在栈上,而 `T` 可以指代任何类型(严格来说是实现了 `PartialOrd` 特征的所有类型) 。
因此, 为了让T拥有 `Copy` 特性,我们可以增加特征约束:
因此,为了让 ` T` 拥有 `Copy` 特性,我们可以增加特征约束:
```rust
fn largest< T: PartialOrd + Copy > (list: & [T]) -> T {
let mut largest = list[0];
@ -392,7 +392,7 @@ fn main() {
总之,`derive` 派生出来的是 Rust 默认给我们提供的特征,在开发过程中极大的简化了自己手动实现相应特征的需求,当然,如果你有特殊的需求,还可以自己手动重载该实现。
详细的 `derive` 列表参加 [附录-派生特征](../../appendix/derive.md)。
详细的 `derive` 列表参见 [附录-派生特征](../../appendix/derive.md)。
## 调用方法需要引入特征
在一些场景中,使用 `as` 关键字做类型转换会有比较大的限制,因为你想要在类型转换上拥有完全的控制,例如处理转换错误,那么你将需要 `TryInto` :
@ -415,7 +415,7 @@ fn main() {
上面代码中引入了 `std::convert::TryInto` 特征,但是却没有使用它,可能有些同学会为此困惑,主要原因在于**如果你要使用一个特征的方法,那么你需要引入该特征到当前的作用域中**,我们在上面用到了 `try_into` 方法,因此需要引入对应的特征。
但是Rust又提供了一个非常便利的办法, 即把最常用的标准库中的特征通过[`std::prelude`](std::convert::TryInto)模块提前引入到当前作用域中,其中包括了 `std::convert::TryInto` ,你可以尝试删除第一行的代码 `use ...` ,看看是否会报错.
但是 Rust 又提供了一个非常便利的办法,即把最常用的标准库中的特征通过 [ `std::prelude` ]( std::convert::TryInto ) 模块提前引入到当前作用域中,其中包括了 `std::convert::TryInto` ,你可以尝试删除第一行的代码 `use ...` ,看看是否会报错。
## 几个综合例子