|
|
|
@ -126,7 +126,7 @@ impl Add<Meters> for Millimeters {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这里,是进行`Millimeters + Meters`的操作,因此此时不能再使用默认的RHS,否则就会变成`Millimeters + Millimeters`的形式。使用`Add<Meters>`可以将`RHS`指定为`Meters`,那么`fn add(self, rhs: RHS) `自然而言的变成了`Millimeters`和`Meters`的相加.
|
|
|
|
|
这里,是进行 `Millimeters + Meters` 的操作,因此此时不能再使用默认的 `RHS`,否则就会变成 `Millimeters + Millimeters` 的形式。使用 `Add<Meters>` 可以将 `RHS` 指定为 `Meters`,那么 `fn add(self, rhs: RHS)` 自然而言的变成了 `Millimeters` 和 `Meters` 的相加。
|
|
|
|
|
|
|
|
|
|
默认类型参数主要用于两个方面:
|
|
|
|
|
1. 减少实现的样板代码
|
|
|
|
@ -136,10 +136,10 @@ impl Add<Meters> for Millimeters {
|
|
|
|
|
|
|
|
|
|
对于第二点,也很好理解,如果你在一个复杂类型的基础上,新引入一个泛型参数,可能需要修改很多地方,但是如果新引入的泛型参数有了默认类型,情况就会好很多。
|
|
|
|
|
|
|
|
|
|
归根到底,默认泛型参数,是有用的,但是大多数情况下,咱们确实用不到,当需要用到时,大家再回头来查阅本章即可,**手上有剑,心中不慌**.
|
|
|
|
|
归根到底,默认泛型参数,是有用的,但是大多数情况下,咱们确实用不到,当需要用到时,大家再回头来查阅本章即可,**手上有剑,心中不慌**。
|
|
|
|
|
|
|
|
|
|
## 调用同名的方法
|
|
|
|
|
不同特征拥有同名的方法是很正常的事情,你没有任何办法阻止这一点,甚至除了特征上的同名方法外,在你的类型上,也有同名方法:
|
|
|
|
|
不同特征拥有同名的方法是很正常的事情,你没有任何办法阻止这一点,甚至除了特征上的同名方法外,在你的类型上,也有同名方法:
|
|
|
|
|
```rust
|
|
|
|
|
trait Pilot {
|
|
|
|
|
fn fly(&self);
|
|
|
|
@ -170,7 +170,7 @@ impl Human {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这里,不仅仅两个特征`Pilot`和`Wizard`有`fly`方法,就连实现那两个特征的`Human`元结构体,也拥有一个同名方法`fly`(这世界怎么了,非常这么卷吗?程序员何苦难为程序员,哎)。
|
|
|
|
|
这里,不仅仅两个特征 `Pilot` 和 `Wizard` 有 `fly` 方法,就连实现那两个特征的 `Human` 元结构体,也拥有一个同名方法 `fly` (这世界怎么了,非要这么卷吗?程序员何苦难为程序员,哎)。
|
|
|
|
|
|
|
|
|
|
既然代码已经不可更改,那下面我们来讲讲该如何调用这些 `fly` 方法。
|
|
|
|
|
|
|
|
|
@ -195,14 +195,14 @@ fn main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
运行后依次输出:
|
|
|
|
|
运行后依次输出:
|
|
|
|
|
```console
|
|
|
|
|
This is your captain speaking.
|
|
|
|
|
Up!
|
|
|
|
|
*waving arms furiously*
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
因为`fly`方法的参数是`self`,当显示的调用时,编译器就可以根据调用的类型(`self`的类型)决定具体调用哪个方法。
|
|
|
|
|
因为 `fly` 方法的参数是 `self`,当显式调用时,编译器就可以根据调用的类型( `self` 的类型)决定具体调用哪个方法。
|
|
|
|
|
|
|
|
|
|
这个时候问题又来了,如果方法没有 `self` 参数呢?稍等,估计有读者会问:还有方法没有 `self` 参数?看到这个疑问,作者的眼泪不禁流了下来,大明湖畔的[关联函数](../method.md#关联函数),你还记得嘛?
|
|
|
|
|
|
|
|
|
@ -240,7 +240,7 @@ fn main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
铛铛,无情报错了:
|
|
|
|
|
铛铛,无情报错了:
|
|
|
|
|
```rust
|
|
|
|
|
error[E0283]: type annotations needed // 需要类型注释
|
|
|
|
|
--> src/main.rs:20:43
|
|
|
|
@ -253,18 +253,18 @@ error[E0283]: type annotations needed // 需要类型注释
|
|
|
|
|
|
|
|
|
|
因为单纯从 `Animal::baby_name()` 上,编译器无法得到任何有效的信息:你想获取哪个动物宝宝的名称?狗宝宝?猪宝宝?还是熊宝宝?
|
|
|
|
|
|
|
|
|
|
此时,就需要使用**完全限定语法**.
|
|
|
|
|
此时,就需要使用**完全限定语法**。
|
|
|
|
|
|
|
|
|
|
##### 完全限定语法
|
|
|
|
|
完全限定语法是调用函数最为明确的方式:
|
|
|
|
|
完全限定语法是调用函数最为明确的方式:
|
|
|
|
|
```rust
|
|
|
|
|
fn main() {
|
|
|
|
|
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
在尖括号中,通过`as`关键字,我们向Rust编译器提供了类型注解,也就是`Animal`就是`Dog`,而不是其他动物,因此最终会调用`impl Animal for Dog `中的方法,获取到其它动物对狗宝宝的称呼:**puppy**.
|
|
|
|
|
在尖括号中,通过 `as` 关键字,我们向Rust编译器提供了类型注解,也就是 `Animal` 就是 `Dog`,而不是其他动物,因此最终会调用 `impl Animal for Dog` 中的方法,获取到其它动物对狗宝宝的称呼:**puppy**。
|
|
|
|
|
|
|
|
|
|
言归正题,完全限定语法定义为:
|
|
|
|
|
言归正题,完全限定语法定义为:
|
|
|
|
|
```rust
|
|
|
|
|
<Type as Trait>::function(receiver_if_method, next_arg, ...);
|
|
|
|
|
```
|
|
|
|
@ -294,7 +294,7 @@ trait OutlinePrint: Display {
|
|
|
|
|
|
|
|
|
|
等等,这里有一个眼熟的语法: `OutlinePrint: Display`,感觉很像之前讲过的**特征约束**,只不过用在了特征定义中而不是函数的参数中,是的,在某种意义上来说,这和特征约束非常类似,都用来说明一个特征需要实现另一个特征,这里就是:如果你想要实现 `OutlinePrint` 特征,首先你需要实现 `Display` 特征。
|
|
|
|
|
|
|
|
|
|
想象一下,假如没有这个特征约束,那么`self.to_string`还能够调用吗(`to_string`方法会为实现`Display`特征的类型自动实现)?编译器肯定是不愿意的,会报错说当前作用域中找不到用于`&Self`类型的方法`to_string`:
|
|
|
|
|
想象一下,假如没有这个特征约束,那么 `self.to_string` 还能够调用吗( `to_string` 方法会为实现 `Display` 特征的类型自动实现)?编译器肯定是不愿意的,会报错说当前作用域中找不到用于 `&Self` 类型的方法 `to_string` :
|
|
|
|
|
```rust
|
|
|
|
|
struct Point {
|
|
|
|
|
x: i32,
|
|
|
|
|