Beginng_Rust(译):使用数据序列(第五章)

在本章中,您将学习:

•如何定义相同类型的对象序列,具有固定长度(数组)或可变长度(向量)

•如何通过列出项目或指定一个项目及其重复计数来指定数组或向量的初始内容

•如何读取或写入单个数组或向量的值

•如何将项目添加到矢量或从矢量中删除项目

•如何创建具有多个维度的数组

•如何创建空数组或向量

•如何打印或复制整个数组或向量

数组

到目前为止,我们已经看到了如何在变量中存储字符串,数字或布尔值。 如果要在单个变量中存储多个字符串,可以编写:

let x = ["English", "This", "sentence", "a", "in", "is"];
print!("{} {} {} {} {} {}",
x[1], x[5], x[3], x[2], x[4], x[0]);

将打印:“This is a sentence in English。”
第一个语句将x变量声明为一个不可变对象,该对象由六个对象组成,这些对象全部是字符串类型,在语句本身中指定。 这种对象确实被命名为“数组”。

第二个语句包含六个表达式,每个表达式都对x的不同元素进行读访问。 这种访问通过括号之间的位置索引或下标来指定要访问的项。 请注意,索引始终从零开始,因此,由于数组有六个元素,因此最后一项的索引为5.此行为类似于C语言数组的行为。

要确定数组中有多少元素,可以执行以下操作:

let a = [true, false];
let b = [1, 2, 3, 4, 5];
print!("{}, {}.", a.len(), b.len());

它将打印“2,5”。

a变量是两个布尔值的数组,而b变量是一个包含五个整数的数组。

第三个语句在对象a和b上调用标准库的len函数,以获取数组中包含的对象的数量。 这些调用的语法和语义都类似于表达式“abc”.len()的语法和语义,用于获取字符串的字节长度。

请注意,在示例中,每个数组都包含相同类型的元素; 只有字符串,只有布尔值,或只有整数。 如果你试着写:

let x = ["This", 4];
or
let x = [4, 5.];

您收到编译错误,表明该数组不能包含不同类型的对象。

可以创建多种类型的项目的数组,只要在每个数组中所有项目都是相同类型。

就是这样,因为没有一种类型的“数组”。 “数组”的概念是泛型类型的概念,它通过其项类型以及其项的数量进行参数化。 因此,在本章的第一个例子中,变量x的类型是“5个字符串的数组”; 而在第二个例子中,a是“2个布尔数组”类型; 和b的类型是“5个整数数组”。

在以下程序中,除第一行之外的每一行都将生成编译错误:

let mut x = ["a"]; // array of strings
x[0] = 3;
x[-1] = "b";
x[0.] = "b";
x[false] = "b";
x["0"] = "b";

第二个语句是错误的,因为它试图为字符串数组的anitem分配一个整数。 以下语句是错误的,因为索引不是非负整数,并且数组索引必须是大于或等于零的整数。

以下是不同的情况:

let x = ["a"]; // array of strings
let _y = x[1];

编译器允许使用此语句,即使我们知道在只有一个项目的数组中读取索引1项(第二项)也没有意义。

但是,当程序运行时,它的机器代码在访问该数组项之前,检查这样的索引是否有效,如果在这种情况下索引无效,它将终止程序的执行,从而发出运行时 错误信息。

请注意,这种异常终止不是由操作系统引起的,而是由Rust编译器插入可执行程序的指令引起的(换句话说,它不是“分段违规陷阱”,而是“堕胎”)。 通过这种方式,程序在每次访问数组时都会检查用于此类访问的索引是否有效,如果它意识到索引超出了数组边界,则会自动终止执行。

在Rust术语中,这种程序执行的提前终止被称为“恐慌”,终止程序的行为被称为“恐慌”。 通过这种方式,每个数组访问都有明确定义的行为,而众所周知,在C语言中,超出范围的数组访问具有未定义的行为。

但是,编译器不能总是检查这样的索引,因此它生成一个可执行程序。 实际上,在这种情况下,由于索引是一个常量值,并且数组大小始终是常量,编译器可以检查此索引是否无效,因此它会发出警告:此表达式将在运行时出现混乱,然后索引出 bounds:len为1但索引为1。

可变数组

只有在可变数组上才能修改数组的项:

let mut x = ["This", "is", "a", "sentence"];
x[2] = "a nice";
print!("{} {} {} {}.", x[0], x[1], x[2], x[3]);

这将打印“This is a nice sentence.”

第一个句子包含“mut”关键字,第二个语句将新字符串分配给数组的第三个项目。 编译器允许此操作,因为以下三个条件成立:

•x变量是可变的。

•分配的新值的类型与x的其他项目的类型相同。 实际上,它们都是字符串。
•索引是非负整数。

此外,在运行时,执行操作时不会发生恐慌,因为索引位于数组边界之间,即条件“0 <= 2”和“2 <4”保持不变。

相反,不允许将项添加到数组中,也不允许从数组中删除项。 因此,它的长度是编译时定义的常量。

作为任何可变变量的数组类型的可变变量可以是来自另一个数组的赋值的目标:

let mut x = ["a", "b", "c"];
print!("{}{}{}. ", x[0], x[1], x[2]);
x = ["X", "Y", "Z"];
print!("{}{}{}. ", x[0], x[1], x[2]);
let y = ["1", "2", "3"];
x = y;
print!("{}{}{}.", x[0], x[1], x[2]);

这将打印“abc.XYZ.123。”。

在第一行中,创建一个数组并将其分配给x变量。 在第三行中,创建另一个数组并将其分配给相同的x变量,从而替换所有数组

三个现有字符串。 在第五行中,创建另一个数组并将其分配给y变量。 在第六行中,将此类数组分配给x变量,从而替换三个现有值。

如果x不可变,则会产生两个编译错误:一个在第三行,一个在第六行。

以下代码在第二行和第三行生成编译错误:

let mut x = ["a", "b", "c"];
x = ["X", "Y"];
x = [15, 16, 17];

实际上,由于第一行,x的类型为“字符串类型的三个元素的数组”。 第二行中的语句尝试为x赋值类型为“字符串类型的两个元素的数组”; 而第三行中的语句尝试为x赋值类型为“三个整数类型元素的数组”的值。 在第一种情况下,即使每个元素的类型正确,元素的数量也是错误的; 相反,在第二种情况下,元素的数量是正确的,但每个元素的类型是错误的。 在这两种情况下,分配的整个值的类型与目标变量的类型不同。

指定大小的数组

我们已经看到了如何通过列出最初包含的项来创建数组。

如果你想处理很多项目,而不是写很多表达式,你可以写:

let mut x = [4.; 5000];
x[2000] = 3.14;
print!("{}, {}", x[1000], x[2000]);

那将打印。 “4,3.14”。

第一个语句将x变量声明为5000个浮点数的可变数组,最初都等于4.注意在方括号之间使用分号,而不是逗号。

第二个语句将值3.14赋给此类数组的位置2000处的项。

最后,打印在位置1000处从未改变的值和在位置2000处刚刚改变的值。 请注意,此数组的有效索引从0到4999。

要扫描数组的项目,“for”语句非常有用:

let mut fib = [1; 15];
for i in 2..fib.len() {
fib[i] = fib[i - 2] + fib[i - 1];
}
for i in 0..fib.len() {
print!("{}, ", fib[i]);
}

这将打印:“1,1,2,3,5,8,13,21,34,55,89,144,233,377,610”。

该程序计算着名的Fibonacci序列的前15个数字,然后打印出来。 这样的序列以这种方式定义:前两个数字都是1,每隔一个数字是前面两个数字的总和。

检查程序。

第一个语句创建一个名为fib的变量,并使用15个数字1的数组对其进行初始化。

以下三行中的语句是for循环,其中i变量初始化为2并且它递增到14.此循环的主体分配给数组的每个项目,从第三个开始,总和 前面两个项目。 鉴于此,当写入每个项目时,前面的项目已经收到了正确的值,这样的赋值始终使用正确的值。

最后还有另一个“for”循环,但这次它的索引从0开始。

多维数组

您可以轻松编写具有多个维度的数组:

let mut x = [[[[23; 4]; 6]; 8]; 15];
x[14][7][5][3] = 56;
print!("{}, {}", x[0][0][0][0], x[14][7][5][3]);

这将打印:“23,56。”

第一个语句声明一个包含15个项目的数组,每个项目是一个包含8个项目的数组,每个项目都是一个包含6个项目的数组,每个项目都是一个包含4个项目的数组,每个项目都使用整数23进行初始化。

第二个语句访问这样的数组的第15个和最后一个项,所以得到一个数组; 然后它访问这样的数组的第8个和最后一个项,所以得到一个数组; 然后访问这样的数组的第6个和最后一个项,所以得到一个数组; 然后访问这样的数组的第4个和最后一个项,所以得到一个整数类型的项; 最后,它将整数56分配给这样的项目。

第三个语句打印第一个项目的内容和数组最后一项的内容。

因为多维数组只不过是数组的数组,所以很容易得到给定数组的大小:

let x = [[[[0; 4]; 6]; 8]; 15];
print!("{}, {}, {}, {}.",
x.len(), x[0].len(), x[0][0].len(), x[0][0][0].len());

这将打印:“15,8,6,4”。
数组的一个很大的限制是它们的大小必须在编译时定义。

let length = 6;
let arr = [0; length];

此代码的编译生成错误尝试在常量中使用非常量值。 实际上表达式长度是一个变量,因此在概念上它不是编译时常量,即使它是不可变的,即使它刚刚被常量初始化。 数组的大小不能是包含变量的表达式。

矢量(列表)

要创建大小在运行时定义的对象序列,Rust标准库提供Vec类型,即向量的简写。

let x = vec!["This", "is"];
print!("{} {}. Length: {}.", x[0], x[1], x.len());

这将打印:“这是。长度:2。”

第一个语句看起来像创建一个不可变数组,唯一不同的是vec的外观! 条款。

这样的子句是对标准库的“vec”宏的调用。 “vec”也是“vector”的缩写。

这种宏的作用确实是创建一个向量,最初包含方括号之间指定的两个字符串。 实际上,当在这样的对象上调用len()时,返回2。

向量允许我们完成数组允许的所有操作,但它们也允许我们在初始化后更改它们的大小:

let mut x = vec!["This", "is"]; print!("{}", x.len());
x.push("a"); print!(" {}", x.len());
x.push("sentence"); print!(" {}", x.len());
x[0] = "That";
for i in 0..x.len() { print!(" {}", x[i]); }

这将打印:“2 3 4这是一个句子。”

第一行创建一个可变向量,最初包含两个字符串,并打印其长度。

第二行在刚刚创建的向量上调用push函数,并打印其新长度。 这样的函数在向量的底部添加了它的参数。 为了合法,它必须只有一个参数,并且这样的参数必须与向量的项目具有相同的类型。 单词“push”是通常用于将项添加到“堆栈”数据结构的操作的单词。

第三行在向量的末尾添加另一个字符串,因此使向量包含四个项目,并打印向量的新长度。

第四行替换向量的第一项的值。 允许此操作和前两个操作仅因为x变量是可变的。 第五行扫描向量的四个项目并将它们全部打印在终端上。
让我们看另一个例子:

let length = 5000;
let mut y = vec![4.; length];
y[6] = 3.14;
y.push(4.89);
print!("{}, {}, {}", y[6], y[4999], y[5000]);

猜你喜欢

转载自blog.csdn.net/m0_37696990/article/details/82795843