一、变量与可变性
默认情况下变量是不可变的。Rust 提供的安全性和简单的并发性来编写代码,变量默认是不可变的。但是,仍然可以选择使变量可变。
当变量不可变时,一旦将值绑定到名称,就无法更改该值。fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
保存并运行程序cargo run
。会收到一条错误消息,如下面的输出所示:cargo run
Compiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| -
| |
| first assignment to `x`
| help: consider making this binding mutable: `mut x`
3 | println!("The value of x is: {}", x);
4 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` due to previous error
错误消息表明错误的原因是cannot assign twice to immutable variable x
,因为试图为不可变x变量分配第二个值。
在 Rust 中,编译器保证当声明一个值不会改变时,它是真的不会改变。这意味着当阅读和编写代码时,不必跟踪值可能发生变化的方式和位置。因此,的代码更容易推理。
不过可变性非常有用。变量仅在默认情况下是不可变的;可以通过mut
在变量名前面添加来使它们可变。除了允许更改此值之外,mut通过指示代码的其他部分将更改此变量的值来向代码的未来读者传达意图。
|
运行程序时,得到:cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
通过 mut
,允许把绑定到 x 的值从 5 改成 6 。在一些情况下,会想用可变变量,因为这样的代码比起只用不可变变量的实现更容易编写。
变量和常量之间的差异
不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:常量 (constants)。类似于不可变变量,常量也是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
- 首先,不能与
mut
常量一起使用。默认情况下,常量不仅仅是不可变的——它们总是不可变的。 - 声明常量使用
const
关键字而不是let
,并且必须注明值的类型。 - 常量可以在任何作用域声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。
- 最后一个区别是常量只能用于常量表达式,而不能作为函数调用的结果,或任何其他只在运行时计算的值。
下面是常量声明的示例,其中常量的名称是 THREE_HOURS_IN_SECONDS
,其值设置为 60(一分钟内的秒数)乘以 60(一小时内的分钟数)乘以 3(小时数)的结果我们想在这个程序中计数):const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
Rust 的常量命名约定是在单词之间使用所有大写字母和下划线。编译器能够在编译时评估一组有限的操作,这让我们可以选择以更容易理解和验证的方式写出这个值,而不是将此常量设置为值 10,800。
隐藏(遮蔽)
可以声明一个与前一个变量同名的新变量。称为第一个变量被第二个变量遮蔽,这意味着第二个变量的值是程序在使用该变量时看到的值。通过使用相同变量的名称并重复使用let关键字来隐藏变量,如下所示:fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x is: {}", x);
}
println!("The value of x is: {}", x);
}
该程序首先绑定x到的值5。然后重复let x =
进行隐藏,取原始值并相加1
,因此的值为 6
。然后,在内部作用域内,第三个let语句也隐藏x,将前一个值乘以2得到x = 12
。当该范围结束时,内部阴影结束并x恢复为6。当我们运行这个程序时,它会输出以下内容:cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6
隐藏与将变量标记为mut
不同,因为如果我们不小心尝试重新分配给这个变量而不使用let关键字,我们将得到一个编译时错误。通过使用let,我们可以对一个值执行一些转换,但在这些转换完成后变量是不可变的。
mut和隐藏之间的另一个区别是,因为使用let再次使用关键字时,有效地创建了一个新变量,可以更改值的类型但重用相同的名称。例如,假设我们的程序要求用户通过输入空格字符来显示他们想要在某些文本之间有多少空格,但我们真的想将该输入存储为一个数字:let spaces = " ";
let spaces = spaces.len();
这种构造是允许的,因为第一个spaces变量是字符串类型,第二个spaces变量是一个全新的变量,碰巧与第一个变量同名,是一个数字类型。因此,隐藏使我们不必想出不同的名称,例如spaces_str和 spaces_num;相反,如果我们尝试使用mut它,如下所示,我们将得到一个编译时错误:let mut spaces = " ";
spaces = spaces.len();
错误说我们不允许改变变量的类型:cargo run
Compiling variables v0.1.0 (file:///projects/variables)
error[E0308]: mismatched types
--> src/main.rs:3:14
|
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected `&str`, found `usize`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` due to previous error
二、数据类型
在 Rust 中,将其分为两类:标量(scalar)和复合(compound)。
Rust 是静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型,这一点将贯穿整个章节。通过值的形式及其使用方式,编译器通常可以推断出我们想要用的类型。let guess: u32 = "42".parse().expect("Not a number!");
如果不在这里添加类型注解,Rust 将显示以下错误,编译器需要我们提供更多信息才能知道我们要使用哪种类型:cargo build
Compiling no_type_annotations v0.1.0 (file:///projects/no_type_annotations)
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number!");
| ^^^^^ consider giving `guess` a type
For more information about this error, try `rustc --explain E0282`.
error: could not compile `no_type_annotations` due to previous error
标量类型
标量(scalar)类型代表一个单独的值。Rust 有四种基本的标量类型:整型
、浮点型
、布尔类型
和字符类型
。
整数类型
一个整数是没有小数部分的数。例如u32类型
,这个类型声明表明它所关联的值应该是一个占用 32 位比特位的无符号整数(有符号整数类型以i,而不是开头u)。下面显示了 Rust 中的内置整数类型。Signed 和 Unsigned 列中的每个变体(例如,i16)都可用于声明整数值的类型。
长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
arch | isize | usize |
每一种变体都可以是有符号或无符号的,并有一个明确的大小。有符号和无符号代表数字能否为负值;换句话说,数字是否需要有一个符号(有符号数),或者永远为正而不需要符号(无符号数)。
这就像在纸上写数字一样:当符号很重要时,数字会用加号或减号显示;但是,当可以安全地假设该数字为正数时,它不会显示任何符号。有符号数使用二进制补码表示存储。
每一个有符号的变体可以储存包含从 -(2^(n - 1)) 到 2^(n - 1) - 1 在内的数字,这里 n 是变体使用的位数。所以 i8 可以储存从 -(2^7) 到 2^7 - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从0到2^n-1的数字,所以 u8 可以储存从0到2^8-1的数字,也就是从0到 255。
此外,isize和usize类型取决于运行程序的计算机类型:64位(如果使用64位架构)和 32 位(如果您使用 32 位架构)。
可以使用下表中的任何一种形式编写数字字面值。注意除 byte 以外的其它字面值允许使用类型后缀,例如 57u8 ,同时也允许使用 _ 做为分隔符以方便读数,例如 1_000 。
数字字面值 | 例子 |
---|---|
十进制 | 98_222 |
十六进制 | 0xff |
八进制 | 0o77 |
二进制 | 0b1111_0000 |
字节(仅u8) | b’A’ |
那么怎么知道使用哪种类型的整数呢?如果不确定,整数类型默认为i32,使用isize或的主要情况usize是索引某种集合时。
整数溢出
例如一个u8
,可以存放0~255的值,但是放入256会发生什么呢?此时为整数溢出。在 debug 模式编译时,Rust 检查这类问题并使程序 panic用来表明程序因错误而退出。
在 release 构建中,Rust 不检测溢出,相反会进行一种被称为二进制补码包装(two’s complement wrapping)的操作。值 256 变成 0,值 257 变成 1,依此类推。依赖整型溢出被认为是一种错误,即便可能出现这种行为。如果你确实需要这种行为,标准库中有一个类型显式提供此功能,Wrapping。 为了显式地处理溢出的可能性,你可以使用标准库在原生数值类型上提供的以下方法:
- 所有模式下都可以使用 wrapping_* 方法进行包装,如 wrapping_add
- 如果 check_* 方法出现溢出,则返回 None值
- 用 overflowing_* 方法返回值和一个布尔值,表示是否出现溢出
- 用 saturating_* 方法在值的最小值或最大值处进行饱和处理
浮点类型
Rust 也有两种用于浮点数的原始类型,它们是带小数点的数字。Rust 的浮点类型是f32
,f64
,它们的大小分别是 32 位和 64 位。默认类型是f64,因为在现代 CPU 上它的速度大致相同,它与 f32 速度几乎一样,不过精度更高。所有的浮点型都是有符号的。fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
浮点数根据 IEEE-754 标准表示。f32类型是单精度浮点数,f64具有双精度。
数值运算
Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和余数。整数除法向下舍入到最接近的整数。以下代码显示了如何在let语句中使用每个数字运算:fn main() {
// addition
let sum = 5 + 10;
// subtraction
let difference = 95.5 - 4.3;
// multiplication
let product = 4 * 30;
// division
let quotient = 56.7 / 32.2;
let floored = 2 / 3; // Results in 0
// remainder
let remainder = 43 % 5;
}
布尔类型
与大多数其他编程语言一样,Rust 中的布尔类型有两个可能的值:true和false。布尔值大小为 1 个字节。Rust 中的布尔类型使用bool.例如:fn main() {
let t = true;
let f: bool = false;
}
字符类型
Rust 的 char类型是该语言中最原始的字母类型,下面的代码展示了使用它的一种方式。(请注意,char文字用单引号指定,而不是使用双引号的字符串文字。)fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
}
Rust 的char类型大小为四个字节,表示一个 Unicode 标量值,这意味着它可以表示的不仅仅是 ASCII。
复合类型
复合类型可以将多个值分组为一种类型。Rust 有两种原始的复合类型:元组和数组。
元组类型
元组是将具有多种类型的多个值组合成一个复合类型的通用方法。元组有固定的长度:一旦声明,它们的大小就不能增长或缩小。
我们通过在括号内写一个逗号分隔的值列表来创建一个元组。元组中的每个位置都有一个类型,元组中不同值的类型不必相同。我们在这个例子中添加了可选的类型注释:fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
该变量tup
绑定到整个元组,因为元组被视为单个复合元素。为了从元组中获取单个值,我们可以使用模式匹配来解构元组值,如下所示:fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
程序首先创建了一个元组并绑定到 tup 变量上。接着使用了 let 和一个模式将 tup 分成了三个不同的变量,x、y 和 z。这叫做 解构(destructuring),因为它将一个元组拆成了三个部分。最后,程序打印出了 y 的值,也就是 6.4。
除了通过模式匹配进行解构之外,我们还可以通过使用句点(.)后跟我们想要访问的值的索引来直接访问元组元素。例如:fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
元组中的第一个索引是0。
没有任何值的元组()是一种特殊类型,只有一个值,也写作()。类型称为单位类型,值称为单位值。如果表达式不返回任何其他值,则表达式会隐式返回单位值。
数组类型
拥有多个值的集合的另一种方法是使用数组。与元组不同,数组的每个元素都必须具有相同的类型。Rust 中的数组与其他一些语言中的数组不同,因为 Rust 中的数组具有固定长度,就像元组一样。
在 Rust 中,进入数组的值被写为方括号内的逗号分隔列表:fn main() {
let a = [1, 2, 3, 4, 5];
}
希望将数据分配在堆栈而不是堆上或者希望确保始终拥有固定数量的元素时,数组很有用。但是,数组不如vector
向量类型灵活。他是由标准库提供一个类似集合类型是允许生长或尺寸的缩小。如果不确定是使用数组还是向量,可能应该使用vector
。
使用方括号编写数组的类型,方括号内包含每个元素的类型、分号,然后是数组中的元素数,如下所示:let a: [i32; 5] = [1, 2, 3, 4, 5];
这里,i32是每个元素的类型。分号后的数字5 表示数组包含五个元素。
以这种方式编写数组的类型看起来类似于初始化数组的另一种语法:如果想创建一个数组,其中每个元素都包含相同的值,可以指定初始值,后跟一个分号,然后是长度方括号中的数组,如下所示:let a = [3; 5];
命名的数组a将包含5所有3最初设置为该值的元素。与写作相同,let a = [3, 3, 3, 3, 3]; 方式更简洁。
- 访问数组元素
数组是可以在堆栈上分配的已知固定大小的单个内存块。可以使用索引访问数组的元素,如下所示:fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
} - 无效的数组元素访问
如果尝试访问超出数组末尾的数组元素,会发生什么情况?thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
三、函数
函数在 Rust 代码中很普遍。该语言中最重要的函数之一:main函数,它是许多程序的入口点。还看到了fn
关键字,声明新函数。
Rust 代码使用蛇形大小写作为函数和变量名称的常规样式。所有字母都是小写,并用下划线分隔单词。这是一个包含示例函数定义的程序:fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
参数
函数也可以定义为具有参数,这些参数是作为函数签名一部分的特殊变量。当一个函数有参数时,可以为它提供这些参数的具体值。
下面的重写版本another_function显示了 Rust 中的参数是什么样的:fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
声明another_function有一个名为的参数x。的类型x指定为i32。当5传递给时another_function,println!宏将5放置一对大括号在格式字符串中的位置。
在函数签名中,必须声明每个参数的类型。这是 Rust 设计中的一个深思熟虑的决定:在函数定义中要求类型注释意味着编译器几乎不需要你在代码的其他地方使用它们来确定你的意思。
当想让一个函数有多个参数时,用逗号分隔参数声明,像这样:fn main() {
print_labeled_measurement(5, 'h');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {}{}", value, unit_label);
}
语句和表达式
函数体由一系列的语句和一个可选的结尾表达式构成。因为 Rust 是一门基于表达式(expression-based)的语言,这是一个需要理解的(不同于其他语言)重要区别。其他语言并没有这样的区别,所以让我们看看语句与表达式有什么区别以及这些区别是如何影响函数体的。
调用宏是一个表达式。我们用来创建新作用域的块{}是一个表达式,例如:fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
这个表达:{
let x = 3;
x + 1
}
是一个块,在这种情况下,计算结果为4。该值被绑定y 为let语句的一部分。请注意x + 1末尾没有分号的行,这与您目前看到的大多数行不同。表达式不包括结束分号。如果在表达式的末尾添加分号,则将其转换为语句,然后该语句不会返回值。在接下来探索函数返回值和表达式时,请记住这一点。
具有返回值的函数
函数可以将值返回给调用它们的代码。我们不命名返回值,但我们确实在箭头(->
)后声明它们的类型。在 Rust 中,函数的返回值与函数体块中最终表达式的值是同义词。可以通过使用return关键字并指定值从函数中提前返回,但大多数函数会隐式返回最后一个表达式。下面是一个返回值的函数示例:fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
函数中没有函数调用、宏,甚至没有let语句five——只有数字5本身。这是 Rust 中一个完全有效的函数。请注意,函数的返回类型也被指定为->i32
。
five 函数的返回值是 5,这就是为什么返回类型i32。让我们更详细地研究一下。有两个重要的部分:首先,该行let x = five();显示我们正在使用函数的返回值来初始化变量。由于该函数five返回 a 5,因此该行与以下内容相同:let x = 5;
但是如果我们在包含的行的末尾放置一个分号x + 1,将它从表达式更改为语句,我们将得到一个错误。fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1;
}
编译此代码会产生错误,如下所示:cargo run
Compiling functions v0.1.0 (file:///projects/functions)
error[E0308]: mismatched types
--> src/main.rs:7:24
|
7 | fn plus_one(x: i32) -> i32 {
| -------- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
8 | x + 1;
| - help: consider removing this semicolon
For more information about this error, try `rustc --explain E0308`.
error: could not compile `functions` due to previous error
主要错误消息“类型不匹配”揭示了此代码的核心问题。函数的定义plus_one说它将返回一个 i32,但语句不会计算出一个值,该值由()单位类型表示。因此,不会返回任何内容,这与函数定义相矛盾并导致错误。在这个输出中,Rust 提供了一条消息,可能有助于纠正这个问题:它建议删除分号,这将修复错误。
四、注释
|
五、控制流
根据条件是否为真决定是否运行某些代码以及在条件为真时决定重复运行某些代码是大多数编程语言的基本构建块。让您控制 Rust 代码执行流程的最常见构造是if表达式和循环。
if 表达式
一种if表达式允许您分支根据条件的代码。您提供一个条件,然后声明,“如果满足此条件,请运行此代码块。如果不满足条件,请不要运行此代码块。”fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
还值得注意的是,此代码中的条件必须是bool
. 如果条件不是bool,我们会得到一个错误。例如,尝试运行以下代码:fn main() {
let number = 3;
if number {
println!("number was three");
}
}
该if条件评估为的值3。此时,rust引发错误:cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if number {
| ^^^^^^ expected `bool`, found integer
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` due to previous error
Rust 不会自动尝试将非布尔类型转换为布尔类型。您必须明确并始终提供 if布尔值作为其条件。
处理多个条件 else if
您可以通过在表达式中组合if和来拥有多个条件。例如:else if
。fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
使用if的let语句
因为if是一个表达式,我们可以在let 语句的右侧使用它。fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {}", number);
}
该number变量将绑定到基于if 表达式结果的值。运行这段代码看看会发生什么:cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/branches`
The value of number is: 5
代码块的计算结果为其中的最后一个表达式,而数字本身也是表达式。在这种情况下,整个if表达式的值取决于执行的代码块。
看看下面的代码:fn main() {
let condition = true;
let number = if condition { 5 } else { "six" };
println!("The value of number is: {}", number);
}
当我们尝试编译这段代码时,我们会得到一个错误。在if和else武器有不兼容的值类型,rust指出来查找程序中的问题:cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:4:44
|
4 | let number = if condition { 5 } else { "six" };
| - ^^^^^ expected integer, found `&str`
| |
| expected because of this
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` due to previous error
if块中的表达式计算为整数,else块中的表达式计算为字符串。这将不起作用,因为变量必须具有单一类型。Rust 需要在编译时number 明确地知道变量是什么类型,因此它可以在编译时验证它的类型在我们使用的任何地方都有效number。如果number仅在运行时确定类型,Rust 将无法做到这一点;如果编译器必须跟踪任何变量的多个假设类型,则编译器会更复杂,并且对代码的保证会更少。
循环
多次执行一个代码块通常很有用。对于这个任务,Rust 提供了几个循环。循环将循环体内部的代码运行到结尾,然后立即从头开始。为了试验循环,让我们创建一个名为loops的新项目。
rust有三种循环:loop
,while
,和for
。让我们一一试试。
重复代码 loop
该loop关键字告诉锈永远比执行的代码块一遍又一遍,或者直到你明确告诉它停止。fn main() {
loop {
println!("again!");
}
}
当我们运行这个程序时,我们会看到again!一遍又一遍地打印,直到我们手动停止程序。
Rust 提供了一种从代码中跳出循环的方法。您可以break在循环内放置关键字以告诉程序何时停止执行循环。continue循环中的关键字告诉程序跳过循环的本次迭代中的任何剩余代码并转到下一次迭代。fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {}", count);
let mut remining = 10;
loop {
println!("remaining = {}", remining);
if remining == 9 {
break;
}
if count == 2 {
break 'counting_up
}
remining -= 1;
}
count += 1;
}
println!("End count = {}", count);
}
外循环有标签’counting_up,它会从 0 到 2 计数。没有标签的内循环从 10 倒数到 9。第一个break没有指定标签的只会退出内循环。该break ‘counting_up;语句将退出外循环。cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
从循环返回值
可以在break用于停止循环的表达式之后添加要返回的值;该值将从循环中返回,以便您可以使用它。fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
}
在循环的每次迭代中,我们都添加1到counter变量中,然后检查计数器是否等于10。当它是时,我们使用break带有 value的 关键字counter * 2。在循环之后,我们使用分号结束为赋值的语句result。最后,我们打印中的值result,在本例中为 20。
条件循环 while
程序在循环中评估条件通常很有用。当条件为真时,循环运行。当条件不再为真时,程序调用break,停止循环。fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
}
循环遍历一个集合 for
您可以使用该while构造循环遍历集合的元素,例如数组。fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}
}
作为更简洁的替代方法,您可以使用for循环并为集合中的每个项目执行一些代码。fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {}", element);
}
}
我们现在提高了代码的安全性,并消除了由于超出数组末尾或未走得足够远而遗漏某些项目而可能导致的错误的可能性。
下面是使用for循环和另一种我们尚未讨论的方法rev来反转范围的倒计时:fn main() {
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}