Update: unified format

pull/367/head
Allan Downey 3 years ago
parent 5003d1c8ae
commit d6675774e5

@ -271,7 +271,7 @@ fn main() {
<Type as Trait>::function(receiver_if_method, next_arg, ...);
```
上面定义中,第一个参数是方法接收器`receiver`(三种`self`),只有方法才拥有,例如关联函数就没有`receiver`。
上面定义中,第一个参数是方法接收器 `receiver` (三种 `self`,只有方法才拥有,例如关联函数就没有 `receiver`
完全限定语法可以用于任何函数或方法调用,那么我们为何很少用到这个语法?原因是 Rust 编译器能根据上下文自动推导出调用的路径,因此大多数时候,我们都无需使用完全限定语法。只有当存在多个同名函数或方法,且 Rust 无法区分出你想调用的目标函数时,该用法才能真正有用武之地。
@ -299,7 +299,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,

@ -22,7 +22,7 @@ fn main() {
}
```
上述代码可以正常运行,但是很啰嗦,如果你要支持更多的类型,那么会更繁琐。程序员或多或少都有强迫症,一个好程序员的公认特征就是 - 懒,这么勤快的写一大堆代码,显然不是咱们的优良传统,是不?
上述代码可以正常运行,但是很啰嗦,如果你要支持更多的类型,那么会更繁琐。程序员或多或少都有强迫症,一个好程序员的公认特征就是 —— 懒,这么勤快的写一大堆代码,显然不是咱们的优良传统,是不?
在开始讲解 Rust 的泛型之前,先来看看什么是多态。
@ -347,7 +347,7 @@ fn main() {
如上所示,我们定义了一个类型为 `[T; N]` 的数组,其中 `T` 是一个基于类型的泛型参数,这个和之前讲的泛型没有区别,而重点在于 `N` 这个泛型参数,它是一个基于值的泛型参数!因为它用来替代的是数组的长度。
`N` 就是const泛型定义的语法是 `const N: usize`表示const泛型N它基于的值类型是 `usize`
`N` 就是 const 泛型,定义的语法是 `const N: usize`,表示 const 泛型 `N` ,它基于的值类型是 `usize`
在泛型参数之前Rust 完全不适合复杂矩阵的运算,自从有了 const 泛型,一切即将改变。

@ -53,7 +53,7 @@ Bingo这个确实是一个办法但是问题来了如果你的对象集
在拥有继承的语言中,可以定义一个名为 `Component` 的类,该类上有一个 `draw` 方法。其他的类比如 `Button`、`Image` 和 `SelectBox` 会从 `Component` 派生并因此继承 `draw` 方法。它们各自都可以覆盖 `draw` 方法来定义自己的行为,但是框架会把所有这些类型当作是 `Component` 的实例,并在其上调用 `draw`。不过 Rust 并没有继承,我们得另寻出路。
## 特征对象定义
为了解决上面的所有问题Rust引入了一个概念 - 特征对象
为了解决上面的所有问题Rust 引入了一个概念 —— **特征对象**
在介绍特征对象之前,先来为之前的 UI 组件定义一个特征:
```rust
@ -231,7 +231,7 @@ fn main() {
#### &dyn和Box\<dyn\>的区别
前文提到, `&dyn``Box<dyn>` 都可以用于特征对象,因此在功能上 `&dyn``Box<dyn>` 几乎没有区别,唯一的区别就是:`&dyn` 减少了一次指针调用。
因为 `Box<dyn>` 是一个宽指针(`fat pointer`),它需要一次额外的解引用后,才能获取到指向`vtable`的指针,然后再通过该指针访问 `vtable` 查询到具体的函数指针,最后进行调用。
因为 `Box<dyn>` 是一个宽指针`fat pointer`,它需要一次额外的解引用后,才能获取到指向 `vtable` 的指针,然后再通过该指针访问 `vtable` 查询到具体的函数指针,最后进行调用。
所以,如果你在乎性能,又想使用特征对象简化代码,可以优先考虑 `&dyn`

@ -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 ...`,看看是否会报错
## 几个综合例子

Loading…
Cancel
Save