Merge pull request #228 from JesseAtSZ/patch-5

Update vector.md
pull/236/head
Sunface 3 years ago committed by GitHub
commit ac3ccfe40e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,7 +2,7 @@
动态数组类型用`Vec<T>`表示,事实上,在之前的章节,它的身影多次出现,我们一直没有细讲,只是简单的把它当作数组处理。
动态数组允许你存储多个值,这些值在内存中一个紧挨着另一个排列,因此访问其中某个元素的成本非常低。动态数组只能存储相同类型的元素,如果你想存储不同类型的元素,可以使用之前讲过的枚举类型或者特征对象.
动态数组允许你存储多个值,这些值在内存中一个紧挨着另一个排列,因此访问其中某个元素的成本非常低。动态数组只能存储相同类型的元素,如果你想存储不同类型的元素,可以使用之前讲过的枚举类型或者特征对象
总之,当我们想拥有一个列表,里面都是相同类型的数据时,动态数组将会非常有用。
@ -15,13 +15,13 @@
let v: Vec<i32> = Vec::new();
```
这里, `v`被显式地声明了类型`Vec<i32>`这是因为Rust编译器无法从`Vec::new()`中得到任何关于类型的暗示信息,因此也无法推导出`v`的具体类型,但是当你向里面增加一个元素后,一切又不同了:
这里`v` 被显式地声明了类型`Vec<i32>`,这是因为 Rust 编译器无法从 `Vec::new()` 中得到任何关于类型的暗示信息,因此也无法推导出 `v` 的具体类型,但是当你向里面增加一个元素后,一切又不同了:
```rust
let mut v = Vec::new();
v.push(1);
```
此时,`v`就无需手动声明类型,因为编译器通过`v.push(1)`,推测出`v`中的元素类型是`i32`,因此推导出`v`的类型是`Vec<i32>`.
此时,`v` 就无需手动声明类型,因为编译器通过 `v.push(1)`,推测出 `v` 中的元素类型是 `i32`,因此推导出 `v` 的类型是 `Vec<i32>`
> 如果预先知道要存储的元素个数,可以使用 `Vec::with_capacity(capacity)` 创建动态数组,这样可以避免因为插入大量新数据导致频繁的内存分配和拷贝,提升性能
@ -34,7 +34,7 @@ let v = vec![1, 2, 3];
同样,此处的 `v` 也无需标注类型,编译器只需检查它内部的元素即可自动推导出 `v` 的类型是 `Vec<i32>` (Rust中整数默认类型是i32在[数值类型](../base-type/numbers.md#整数类型)中有详细介绍)。
## 更新Vector
向数组尾部添加元素,可以使用`push`方法:
向数组尾部添加元素,可以使用 `push` 方法:
```rust
let mut v = Vec::new();
v.push(1);
@ -44,7 +44,7 @@ v.push(1);
## Vector与其元素共存亡
跟结构体一样,`Vector`类型在超出作用域范围后,会被自动删除:
跟结构体一样,`Vector` 类型在超出作用域范围后,会被自动删除
```rust
{
let v = vec![1, 2, 3];
@ -53,10 +53,10 @@ v.push(1);
} // <- v
```
当`Vector`被删除后,它内部存储的所有内容也会随之被删除。目前来看,这种解决方案简单直白,但是当`vector`中的元素被引用后,事情可能会没那么简单。
`Vector` 被删除后,它内部存储的所有内容也会随之被删除。目前来看,这种解决方案简单直白,但是当 `Vector` 中的元素被引用后,事情可能会没那么简单。
## 从Vector中读取元素
读取指定位置的元素有两种方式可选: 通过下标索引访问或者使用`get`方法:
读取指定位置的元素有两种方式可选:通过下标索引访问或者使用 `get` 方法:
```rust
let v = vec![1, 2, 3, 4, 5];
@ -98,7 +98,7 @@ v.push(6);
println!("The first element is: {}", first);
```
先不运行,来推断下结果,首先`first = &v[0]`进行了不可变借用,`v.push`进行了可变借用,如果`first`在`v.push`之后不再使用,那么该段代码可以成功编译(原因见[引用的作用域](../ownership/borrowing.md#可变引用与不可变引用不能同时存在)).
先不运行,来推断下结果,首先 `first = &v[0]` 进行了不可变借用,`v.push` 进行了可变借用,如果 `first` `v.push` 之后不再使用,那么该段代码可以成功编译(原因见[引用的作用域](../ownership/borrowing.md#可变引用与不可变引用不能同时存在))
可是上面的代码中,`first` 这个不可变借用在可变借用 `v.push` 后被使用了,那么妥妥的,编译器就会报错:
```console
@ -122,7 +122,7 @@ error: could not compile `collections` due to previous error
其实,按理来说,这两个引用不应该互相影响的:一个是查询元素,一个是在数组尾部插入元素,完全不相干的操作,为何编译器要这么严格呢?
原因在于:数组的大小是可变的,当老数组的大小不够用时Rust会重新分配一块更大的内存空间然后把老数组拷贝过来。这种情况下之前的引用显然会指向一块无效的内存这非常rusty - 对用户进行严格的教育。
原因在于:数组的大小是可变的,当旧数组的大小不够用时Rust会重新分配一块更大的内存空间然后把旧数组拷贝过来。这种情况下之前的引用显然会指向一块无效的内存这非常rusty - 对用户进行严格的教育。
其实想想,**在长大之后,我们感激人生路上遇到过的严师益友,正是因为他们,我们才在正确的道路上不断前行,虽然在那个时候,并不能理解他们**,而 Rust 就如那个良师益友,它不断的在纠正我们不好的编程习惯,直到某一天,你发现自己能写出一次性通过的漂亮代码时,就能明白它的良苦用心。
@ -130,7 +130,7 @@ error: could not compile `collections` due to previous error
## 迭代遍历Vector中的元素
如果想要依次访问数组中的元素,可以使用迭代的方式去遍历数组,这种方式比用下标的方式去遍历数组更安全也更高效(每次下标访问都会触发数组边界检查):
如果想要依次访问数组中的元素,可以使用迭代的方式去遍历数组,这种方式比用下标的方式去遍历数组更安全也更高效(每次下标访问都会触发数组边界检查)
```rust
let v = vec![1, 2, 3];
for i in &v {
@ -147,7 +147,7 @@ for i in &mut v {
```
## 存储不同类型的元素
在本节开头,有讲到数组的元素必需类型相同,但是也提到了解决方案: 那就是通过使用枚举类型和特征对象来实现不同类型元素的存储。先来看看通过枚举如何实现:
在本节开头,有讲到数组的元素必需类型相同,但是也提到了解决方案那就是通过使用枚举类型和特征对象来实现不同类型元素的存储。先来看看通过枚举如何实现:
```rust
#[derive(Debug)]
enum IpAddr {
@ -203,7 +203,7 @@ fn main() {
}
```
比枚举实现要稍微复杂一些,我们为`V4`和`V6`都实现了特征`IpAddr`,然后将它俩的实例用`Box::new`包裹后,存在了数组`v`中,需要注意的是,这里必需手动的指定类型:`Vec<Box<dyn IpAddr>>`,表示数组`v`存储的是特征`IpAddr`的对象,这样就实现了在数组中存储不同的类型.
比枚举实现要稍微复杂一些,我们为 `V4` `V6` 都实现了特征 `IpAddr`,然后将它俩的实例用 `Box::new` 包裹后,存在了数组 `v` 中,需要注意的是,这里必需手动的指定类型:`Vec<Box<dyn IpAddr>>`,表示数组 `v` 存储的是特征 `IpAddr` 的对象,这样就实现了在数组中存储不同的类型
在实际使用场景中,特征对象数组要比枚举数组常见很多,主要原因在于[特征对象非常灵活](../trait/trait-object.md),而编译器对枚举的限制较多,且无法动态增加类型。

Loading…
Cancel
Save