十三、Rust 数组

虽然我们看到的绝大多数变量都是基本数据类型。虽然这些基本数据类型的能够满足大部分的工作,但它们不是万能的。

基本数据类型的变量也有它们的局限性。

  • 基本数据类型的变量本质上是 标量。这意味着每个基本数据类型的变量一次只能存储一个值。 因此,当我们需要存储多个值的时候,我们不得不重复定义多个变量。比如 a1、a2、a3 …. 如果我们要存储的值非常多,成百上千,这种重复定义变量的方法是行不通的。

  • 基本数据类型的变量的值在内存中的位置是随机的。多个按照顺序定义的变量,它们在内存中的存储位置不一定是连续的。因此我们可能按照变量的声明顺序来获取它们的值。

数组 是用来存储一系列数据,但它往往被认为是一系列相同类型的变量,也就是说,数组 是可以存储一个固定大小的相同类型元素的顺序集合。

数组的声明并不是声明一个个单独的变量,比如 number0、number1、…、number99,而是声明一个数组变量,比如 numbers,然后使用 numbers[0]、numbers[1]、…、numbers[99] 来代表一个个单独的变量。 数组中的特定元素可以通过索引访问

数组可以理解为相同数据类型的值的集合。

13.1 数组的特性

  • 数组的定义其实就是为分配一段 连续的相同数据类型 的内存块。

  • 数组是静态的。这意味着一旦定义和初始化,则永远不可更改它的长度。

  • 数组的元素有着相同的数据类型,每一个元素都独占者数据类型大小的内存块。 也就是说。数组的内存大小等于数组的长度乘以数组的数据类型。

  • 数组中的每一个元素都按照顺序依次存储,这个顺序号既代表着元素的存储位置,也是数组元素的唯一标识。我们把这个标识称之为 数组下标 。 注意,数组下标从 0 开始。

  • 填充数组中的每一个元素的过程称为 数组初始化。也就是说 数组初始化 就是为数组中的每一个元素赋值。

  • 可以更新或修改数组元素的值,但不能删除数组元素。如果要删除功能,你可以将它的值赋值为 0 或其它表示删除的值。

13.2 声明和初始化数组

Rust 语言为数组的声明和初始化提供了 3 中语法

  1. 最基本的语法,指定每一个元素的初始值
  1. let variable_name:[dataType;size] = [value1,value2,value3];

例如

  1. let arr:[i32;4] = [10,20,30,40];
  1. 省略数组类型的语法

因为指定了每一个元素的初始值,所以可以从初始值中推断出数组的类型

  1. let variable_name = [value1,value2,value3];

例如

  1. let arr = [10,20,30,40];

指定默认初始值的语法,这种语法有时候称为 默认值初始化。

如果不想为每一个元素指定初始值,则可以为所有元素指定一个默认的初始值。

  1. let variable_name:[dataType;size] = [default_value_for_elements,size];

例如下面的代码为每一个元素指定初始值为 -1

  1. let arr:[i32;4] = [-1;4];

13.2.1 数组初始化:简单的数组

数组初始化的语法一般如下

  1. let variable_name = [value1,value2,value3];

这种一种最基本的初始化方法,也是字符最长的初始化方法,除了明确指定了数组的类型外,还未每一个数组元素指定了初始值。

数组是一个复合类型,因此输出数组的时候需要使用 {:?} 格式符。

Rust 还提供了 len() 方法则用于返回数组的长度,也就是元素的格式。

  1. fn main(){
  2. let arr:[i32;4] = [10,20,30,40];
  3. println!("array is {:?}",arr);
  4. println!("array size is :{}",arr.len());
  5. }

编译运行以上 Rust 代码,输出结果如下

  1. array is [10, 20, 30, 40]
  2. array size is :4

13.2.2 数组初始化:忽略元素数据类型

数组初始化时如果为每一个元素都指定了它的初始值,那么在定义数组时可以忽略数组的数据类型。

因为这时候,编译器可以通过元素的数据类型自动推断出数组的数据类型。

例如下面的代码,我们的数组长度为 4,因为初始化的时候为 4 个元素都指定了初始值为整型,那么声明数组变量的时候就可以忽略数组的数据类型。

数组的 len() 函数用于返回数组的长度。

  1. fn main(){
  2. let arr = [10,20,30,40];
  3. println!("array is {:?}",arr);
  4. println!("array size is :{}",arr.len());
  5. }

编译运行以上 Rust 范例,输出结果如下

  1. array is [10, 20, 30, 40]
  2. array size is :4

13.3 数组默认值

在数组初始化时,如果不想为数组中的每个元素指定值,我们可以为数组设置一个默认值,也就是使用 默认值初始化 语法。

当使用 默认值初始化 语法初始化数组时,数组中的每一个元素都被设置为默认值。

例如下面的代码,我们将数组中所有的元素的值初始化为 -1

  1. fn main() {
  2. let arr:[i32;4] = [-1;4];
  3. println!("array is {:?}",arr);
  4. println!("array size is :{}",arr.len());
  5. }

编译运行以上 Rust 代码,输出结果如下

  1. array is [-1, -1, -1, -1]
  2. array size is :4

13.4 数组长度 len()

Rust 为数组提供了 len() 方法用于返回数组的长度。

len() 方法的返回值是一个整型。

例如下面的代码,我们使用 len() 求数组的长度

  1. fn main() {
  2. let arr:[i32;4] = [-1;4];
  3. println!("array size is :{}",arr.len());
  4. }

编译运行以上 Rust 代码,输出结果如下

  1. array size is :4

13.5 for in 循环遍历数组

在其它语言中,一般使用 for 循环来遍历数组,Rust 语言也可以,只不过时使用 for 语句的变种 for … in .. 语句。

因为数组的长度在编译时就时已知的,因此我们可以使用 for … in 语句来遍历数组。

注意 for in 语法中的左闭右开法则。

  1. fn main(){
  2. let arr:[i32;4] = [10,20,30,40];
  3. println!("array is {:?}",arr);
  4. println!("array size is :{}",arr.len());
  5. for index in 0..4 {
  6. println!("index is: {} & value is : {}",index,arr[index]);
  7. }
  8. }

编译运行以上 Rust 代码,输出结果如下

  1. array is [10, 20, 30, 40]
  2. array size is :4
  3. index is: 0 & value is : 10
  4. index is: 1 & value is : 20
  5. index is: 2 & value is : 30
  6. index is: 3 & value is : 40

13.6 迭代数组 iter()

我们可以使用 iter() 函数为数组生成一个迭代器。

然后就可以使用 for in 语法来迭代数组。

  1. fn main(){
  2. let arr:[i32;4] = [10,20,30,40];
  3. println!("array is {:?}",arr);
  4. println!("array size is :{}",arr.len());
  5. for val in arr.iter(){
  6. println!("value is :{}",val);
  7. }
  8. }

编译运行以上 Rust 代码,输出结果如下

  1. array is [10, 20, 30, 40]
  2. array size is :4
  3. value is :10
  4. value is :20
  5. value is :30
  6. value is :40

13.7 可变数组

使用 let 声明的变量,默认是只读的,数组也不例外。也就是说,默认情况下,数组是不可变的。

  1. fn main(){
  2. let arr:[i32;4] = [10,20,30,40];
  3. arr[1] = 0;
  4. println!("{:?}",arr);
  5. }

上面的代码运行会出错,错误信息如下

  1. error[E0594]: cannot assign to `arr[_]`, as `arr` is not declared as mutable
  2. --> src/main.rs:3:4
  3. |
  4. 2 | let arr:[i32;4] = [10,20,30,40];
  5. | --- help: consider changing this to be mutable: `mut arr`
  6. 3 | arr[1] = 0;
  7. | ^^^^^^^^^^ cannot assign
  8. error: aborting due to previous error

数组的不可变,表现为两种形式:变量不可重新赋值为其它数组、数组的元素不可以修改。

如果要让数组的元素可以修改,就需要添加 mut 关键字。例如

  1. let mut arr:[i32;4] = [10,20,30,40];

我们将刚刚错误的代码修改下

  1. fn main(){
  2. let mut arr:[i32;4] = [10,20,30,40];
  3. arr[1] = 0;
  4. println!("{:?}",arr);
  5. }

就可以正常运行,输出结果如下

  1. [10, 0, 30, 40]

13.8 数组作为函数参数

数组可以作为函数的参数。而传递方式有 传值传递引用传递 两种方式。

传值传递 就是传递数组的一个副本给函数做参数,函数对副本的任何修改都不会影响到原来的数组。

引用传递 就是传递数组在内存上的位置给函数做参数,因此函数对数组的任何修改都会影响到原来的数组。

13.8.1 范例1:传值传递

下面的代码,我们使用传值方式将数组传递给函数做参数。函数对参数的任何修改都不会影响到原来的数组。

  1. fn main() {
  2. let arr = [10,20,30];
  3. update(arr);
  4. println!("Inside main {:?}",arr);
  5. }
  6. fn update(mut arr:[i32;3]){
  7. for i in 0..3 {
  8. arr[i] = 0;
  9. }
  10. println!("Inside update {:?}",arr);
  11. }

编译运行以上 Rust 代码,输出结果如下

  1. Inside update [0, 0, 0]
  2. Inside main [10, 20, 30]

13.8.2 范例2: 引用传递

下面的代码,我们使用引用方式将数组传递给函数做参数。函数对参数的任何修改都会影响到原来的数组

  1. fn main() {
  2. let mut arr = [10,20,30];
  3. update(&mut arr);
  4. println!("Inside main {:?}",arr);
  5. }
  6. fn update(arr:&mut [i32;3]){
  7. for i in 0..3 {
  8. arr[i] = 0;
  9. }
  10. println!("Inside update {:?}",arr);
  11. }

编译运行以上 Rust 代码,输出结果如下

  1. Inside update [0, 0, 0]
  2. Inside main [0, 0, 0]

13.9 数组声明和常量

数组 ( array ) 有一个唯一的弱点,它的长度必须在编译时就是固定的已知的。

声明数组时长度必须指定为整数字面量或者整数常量。

如果数组长度是一个变量,则会报编译错误。例如下面的代码

  1. fn main() {
  2. let N: usize = 20;
  3. let arr = [0; N]; //错误: non-constant used with constant
  4. print!("{}",arr[10])
  5. }

编译上面的 Rust 代码报错

  1. error[E0435]: attempt to use a non-constant value in a constant
  2. --> main.rs:3:18
  3. |
  4. 3 | let arr = [0; N]; //错误: non-constant used with constant
  5. | ^ non-constant value
  6. error: aborting due to previous error
  7. For more information about this error, try `rustc --explain E0435`.

报错的原因是: N 不是一个常量。

注意,虽然 N 默认是只读的,但它仍然是一个变量,只不过是一个只读变量而已,只读变量不是常量。因为变量的值是在运行时确定的,而常量的值是在编译器确定的。

变量不可用做数组的长度。

如果我们将 let 关键字修改为 const 关键字,编译就能通过了。

  1. fn main() {
  2. const N: usize = 20;
  3. // 固定大小
  4. let arr = [0; N];
  5. print!("{}",arr[10])
  6. }

编译运行以上 Rust 代码,输出结果如下

  1. 0

usize 是一个指针所占用的大小。它的实际大小取决于你编译程序的 cpu 体系结构。