Rust 学习笔记 3:一般性编程概念

上一篇:Rust 学习笔记 2:猜数字游戏

文章目录

  • 1. 前言
  • 2. 背景
  • 3. Rust 中的一般性编程概念
    • 3.1 变量及其可变性(Mutability)
    • 3.1.1 变量定义
      • 3.1.2 常量
      • 3.1.3 变量隐藏(Shadowing)
    • 3.2 基本类型
      • 3.2.1 标量(scalar)类型
        • 3.2.1.1 整型(Integer Types)
        • 3.2.1.2 浮点型(Floating-Point Types)
        • 3.2.1.3 数值运算(Numeric Operations)
        • 3.2.1.4 布尔类型(The Boolean Type)
        • 3.2.1.5 字符类型(The Character Type)
        • 3.2.1.6 复合类型(Compound Types)
          • 3.2.1.6.1 元组(The Tuple Type)
          • 3.2.1.6.2 数组(The Array Type)
            • 3.2.1.6.2.1 访问数组元素
            • 3.2.1.6.2.2 非法访问数组元素
    • 3.3 函数
      • 3.3.1 函数参数
      • 3.3.2 语句 和 表达式
      • 3.3.3 函数返回值
    • 3.4 注释
    • 3.5 控制流
      • 3.5.1 if 表达式
        • 3.5.1.1 在 let 语句中使用 if
      • 3.5.2 循环
        • 3.5.2.1 `loop` 循环
          • 3.5.2.1.1 从 `loop` 循环返回值
          • 3.5.2.1.2 `loop 标签`以消除多个 `loop` 之间的歧义
        • 3.5.2.2 `while` 循环
        • 3.5.2.3 `for` 循环

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 背景

本文基于 Rust 文档 Common Programming Concepts 翻译整理而成。

3. Rust 中的一般性编程概念

本章节描述几乎所有编程语言都会出现的一般性编程概念,包括:

  • 变量
  • 基本类型
  • 函数
  • 注释
  • 控制流

3.1 变量及其可变性(Mutability)

3.1.1 变量定义

Rust 通过 let 关键字来定义变量。如:

let var = 5;

Rust 中定义的变量,默认不可变的(immutable),下面的代码中,语句 x = 6; 试图修改变量 x,将会导致编译错误:

fn main() {let x = 5;println!("The value of x is: {x}");x = 6;println!("The value of x is: {x}");
}
Compiling playground v0.0.1 (/playground)
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

想要将变量定义为可变的(mutable),可以在定义变量时加上 mut 修饰。前面的代码按如下修改,将不再产生编译错误:

fn main() {let mut x = 5;println!("The value of x is: {x}");x = 6;println!("The value of x is: {x}");
}

3.1.2 常量

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

定义常量,需要显式地指定类型。指定常量类型的方式是:在变量名后紧跟 : ,再加上类型指定常量类型的方式也适用于变量

3.1.3 变量隐藏(Shadowing)

fn main() {let x = 5;let x = x + 1;{let x = x * 2;println!("The value of x in the inner scope is: {x}");}println!("The value of x is: {x}");
}

上面这段代码的运行输出如下:

The value of x in the inner scope is: 12
The value of x is: 6

这里有两个关键点:

1. Rust 允许定义同名变量,顺序上定义在后面变量隐藏前面的变量;
2. 变量有其作用域。这和其它语言相似,上面里层 {} 内 x 的在离开 } 时就不再有效了。

Rust 甚至允许改变同名变量的类型:

let spaces = "   ";
let spaces = spaces.len();

这一点和 Python 类似。但下面的代码时不允许的,将会产生编译错误,这又和 Python 不同:

let mut spaces = "   ";
spaces = spaces.len();

3.2 基本类型

Rust 中任何变量都属于一种类型,本文关注标量(scalar)复合数据(compound)类型。 Rust 是一种静态类型语言,也就是说,在编译时编译器必须知道变量的类型,这和 C 语言 一样;和 C 语言不一样的是,Rust 编译器可以根据变量的使用方式,来推断变量的数据类型,但当一个变量在上下文中可以是多种类型时,这时候需要显式指定变量的类型,如:

let guess: u32 = "42".parse().expect("Not a number!");

上面的代码将变量显式地指定为 u32 类型。如果不这样做,像如下编码:

let guess = "42".parse().expect("Not a number!");

编译器将爆出错误信息:

error[E0284]: type annotations needed--> src/main.rs:2:9|
2 |     let guess = "42".parse().expect("Not a number!");|         ^^^^^        ----- type must be known at this point|= note: cannot satisfy `<_ as FromStr>::Err == _`
help: consider giving `guess` an explicit type|
2 |     let guess: /* Type */ = "42".parse().expect("Not a number!");|              ++++++++++++For more information about this error, try `rustc --explain E0284`.
error: could not compile `playground` (bin "playground") due to 1 previous error

错误信息 type annotations needed 明确的昭示,需要显式指定数据类型。

3.2.1 标量(scalar)类型

标量类型表示单个值Rust 有四种主要的标量类型整数、浮点数、布尔值、字符。让我们来看看它们在 Rust 中是如何工作的。

3.2.1.1 整型(Integer Types)

Rust 中的整型(Integer Types)有如下几种:

LengthSignedUnsigned
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

除了 arch 外,其它类型一目了然,没啥好说的。arch 类型数据位数取决于硬件架构:在 32-bit 架构下是 32-bit,在 64-bit 架构下是 64-bit

Rust 中数字常量的书写形式多种多样,如下表所示:

Number literalsExample说明
Decimal98_222用 _ 作为分隔符
Hex0xff十六进制形式
Octal0o77八进制形式
Binary0b1111_0000二进制形式
Byte (u8 only)b’A’字节,仅适用于 u8 类型
3.2.1.2 浮点型(Floating-Point Types)

Rust 的浮点型有 f32f64 两种,默认位 f64。所有浮点都是有符号(signed)类型。看个例子:

fn main() {let x = 2.0; // f64let y: f32 = 3.0; // f32
}

Rust 浮点根据 IEEE 754 标准表示,f32单精度(single-precision)浮点,f64双精度(double precision)浮点。

3.2.1.3 数值运算(Numeric Operations)

Rust 支持你期望的所有数字类型的基本数学运算:加法、减法、乘法、除法、余数。整数除法将向零截断到最接近的整数。以下代码显示了如何在 let 语句中使用每个数值运算:

fn main() {// additionlet sum = 5 + 10;// subtractionlet difference = 95.5 - 4.3;// multiplicationlet product = 4 * 30;// divisionlet quotient = 56.7 / 32.2;let truncated = -5 / 3; // Results in -1// remainderlet remainder = 43 % 5;
}

这些语句中的每个表达式都使用数学运算符,并计算为单个值,然后将其绑定到变量。

3.2.1.4 布尔类型(The Boolean Type)

与大多数其他编程语言一样,Rust 中的布尔类型有两个可能的值:truefalse布尔值的大小为 1 个字节Rust 中的 布尔类型是使用 bool 指定的。例如:

fn main() {let t = true;let f: bool = false; // with explicit type annotation
}
3.2.1.5 字符类型(The Character Type)

Rustchar 类型是该语言最原始的字母类型。以下是声明 char 值的一些示例:

fn main() {let c = 'z';let z: char = 'ℤ'; // with explicit type annotationlet heart_eyed_cat = '😻';
}

请注意,我们使用单引号指定 char 文本,而不是使用双引号的字符串文本。Rustchar 类型大小为 4 个字节,表示一个 Unicode 标量值,这意味着它可以表示的不仅仅是 ASCII。重音字母、中文、日文和韩文字符、表情符号、和零宽度的空格都是 Rust 中的有效 char 值。Unicode 标量值的范围U+0000 到 U+D7FFU+E000 到 U+10FFFF(含)。

3.2.1.6 复合类型(Compound Types)

复合类型可以将一组的多个值作为一种类型。Rust 有两种原始复合类型:元组(Tuple)数组(Array)

3.2.1.6.1 元组(The Tuple Type)

元组(Tuple)是将具有多种类型多个值分组为一种复合类型的通用方法。元组具有固定长度:一旦声明,它们的大小不能增大或缩小

我们通过在括号内编写以逗号分隔的值列表来创建元组。元组中的每个位置都有一个类型,元组中不同值的类型不必相同。在此示例中,我们添加了可选的类型标注

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。这称为解构,因为它将单个元组分成三个部分。最后,程序打印 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;
}

该程序创建元组 x 并使用各自的索引访问元组的每个元素。与大多数编程语言一样,元组中的第一个索引是 0

没有任何值的元组具有特殊名称 unit。此值及其相应的类型都写入 () 并表示空值空返回类型如果表达式不返回任何其他值,则隐式返回 unit 值。

3.2.1.6.2 数组(The Array Type)

拥有多个值的集合的另一种方法是使用数组(array)。与元组(tuple)不同,数组的每个元素都必须具有相同的类型。与其他一些语言中的数组不同,Rust 中的数组具有固定的长度

我们将数组中的值写成方括号内的逗号分隔列表

fn main() {let a = [1, 2, 3, 4, 5];
}

当希望将数据分配在堆栈而不是堆上时,或者当希望确保始终具有固定数量的元素时,数组非常有用。但是,数组不如 vector 类型灵活。向量是标准库提供的类似集合类型,允许其大小增大或缩小。

但是,当知道元素数不需要更改时,数组会更有用。例如,如果在程序中使用月份的名称,你可能会使用数组而不是向量,因为你知道它始终包含 12 个元素:

let months = ["January", "February", "March", "April", "May", "June", "July","August", "September", "October", "November", "December"];

使用方括号指定数组的类型,其中的元素分别是数组的类型数组中的元素个数,如下所示:

fn main() {let a: [i32; 5] = [1, 2, 3, 4, 5];
}

你还可以初始化所有元素为相同值的数组,方法是指定初始值,后跟分号、最后是数组的长度,并将它们置于方括号内,如下所示:

let a = [3; 5];

名为 a 的数组将包含 5 个元素这些元素最初都将设置为值 3。这等同于 let a = [3, 3, 3, 3, 3];,但方式更简洁。

3.2.1.6.2.1 访问数组元素

你可以使用索引访问数组的元素,如下所示:

fn main() {let a = [1, 2, 3, 4, 5];let first = a[0];let second = a[1];
}

在此示例中,名为 first 的变量将获得值 1,因为这是数组中索引 [0] 处的值。名为 second 的变量将从数组中的索引 [1] 获取值 2

3.2.1.6.2.2 非法访问数组元素

让我们看看如果你尝试访问数组中超过数组末尾的元素会发生什么。假设你运行这段代码:

use std::io;fn main() {let a = [1, 2, 3, 4, 5];println!("Please enter an array index.");let mut index = String::new();io::stdin().read_line(&mut index).expect("Failed to read line");let index: usize = index.trim().parse().expect("Index entered was not a number");let element = a[index];println!("The value of the element at index {index} is: {element}");
}

如果使用 cargo run 运行此代码并输入 0、1、2、3 或 4,则程序将打印出数组中该索引处的相应值。如果输入的是一个超过数组末尾的数字,例如 10,您将看到如下输出:

thread 'main' panicked at src/main.rs:19:19:
index out of bounds: the len is 5 but the index is 10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

该程序在索引操作中使用无效值时导致运行时错误。程序退出并显示错误消息,并且未执行最终的 println 语句。当你尝试使用索引访问元素时,Rust 将检查你指定的索引是否小于数组长度。如果索引大于或等于长度,Rustpanic此检查必须在运行时进行,因为编译器不可能知道用户稍后运行代码时将输入什么值

3.3 函数

fn 关键字,用来声明新函数,在函数名后跟一对花括号 {} 用来定义函数体。如:

fn main() {println!("Hello, world!");another_function();
}fn another_function() {println!("Another function.");
}

Rust 不关心你在哪里定义你的函数,只关心它们在调用者可以看到的作用域中的某个位置定义。

3.3.1 函数参数

我们可以将函数定义为带有参数,如下:

fn main() {another_function(5);
}fn another_function(x: i32) {println!("The value of x is: {x}");
}

在函数定义中,必须声明每个参数的类型。这是 Rust 设计中的一个深思熟虑的决定:在函数定义中要求标注类型,意味着编译器几乎不需要通过查看代码中其他位置对它们的使用,来弄清楚它们的类型。如果编译器知道函数需要什么类型,它也能够提供更有用的错误消息。

定义多个参数时,请用逗号分隔参数声明,如下所示:

fn main() {print_labeled_measurement(5, 'h');
}fn print_labeled_measurement(value: i32, unit_label: char) {println!("The measurement is: {value}{unit_label}");
}

3.3.2 语句 和 表达式

函数体由一系列语句(statements)组成,可以选择以表达式(expression)结尾。到目前为止,我们介绍的函数尚未包含结束表达式,但您已经看到表达式作为语句的一部分。因为 Rust 是一种基于表达式的语言,所以这是一个需要理解的重要区别。其他语言没有相同的区别,所以让我们看看什么是语句和表达式,以及它们的区别如何影响函数体。

  • 语句(statements):执行某些操作,但不返回值。
  • 表达式(expression):表达式产生一个结果值。

由于语句(statements)不返回值,所以你不能将 let 语句分配给另一个变量,就像下面的代码尝试执行的操作一样,将会产生编译错误:

fn main() {let x = (let y = 6);
}
error: expected expression, found `let` statement--> src/main.rs:2:11|
2 |     let x = (let y = 6);|              ^^^|= note: only supported directly in conditions of `if` and `while` expressionswarning: unnecessary parentheses around assigned value--> src/main.rs:2:10|
2 |     let x = (let y = 6);|             ^         ^|= note: `#[warn(unused_parens)]` on by default
help: remove these parentheses|
2 -     let x = (let y = 6);
2 +     let x = let y = 6;|

let y = 6 语句不返回值,因此 x 没有要绑定到的任何内容。这与其他语言(如 CRuby)中发生的情况不同,其中赋值返回赋值。在这些语言中,您可以编写 x = y = 6,并且 x 和 y 的值为 6;但在 Rust 中不是这种情况。

表达式(expression)的计算结果为一个值。考虑一个数学运算,例如 5 + 6,它是一个计算结果为 11 的表达式。表达式可以是语句的一部分。调用函数是一个表达式调用宏是一个表达式使用大括号创建的新作用域是一个表达式,例如:

fn main() {let y = {let x = 3;x + 1};println!("The value of y is: {y}");
}

其中:

{let x = 3;x + 1
};

是一个表达式,其计算结果为 4。该值作为 let 语句的一部分绑定到 y。请注意,x + 1 行的末尾没有分号,这与目前看到的大多数行不同。表达式不包括结束分号如果在表达式的末尾添加分号,则会将其转换为语句,并且不会返回值

3.3.3 函数返回值

函数可以将值返回给调用它们的代码。在函数声明的尾部用箭头 -> 声明函数返回值的类型。在 Rust 中,函数的返回值与函数体中最后一个表达式的值同义。可以通过使用 return 关键字并指定值从函数提前返回,但大多数函数都隐式返回最后一个表达式。下面是一个返回值的函数示例:

fn five() -> i32 { // 函数返回值的类型为 i325 // 函数返回值: 5
}fn main() {let x = five();println!("The value of x is: {x}");
}

函数 five() 中没有函数调用、宏,甚至没有 let 语句,只有数字 5 本身。这在 Rust 中是一个完全有效的函数。请注意,该函数的返回类型也被指定为 -> i32。尝试运行此代码,输出应如下所示:

The value of x is: 5

函数 five() 中的 5 是函数的返回值,这就是为什么返回类型为 i32 的原因。让我们更详细地研究一下,有两个重要的部分:首先,语句 let x = five(); 表示正在使用函数的返回值来初始化变量。由于函数 five() 返回 5,因此该行与以下内容相同:

let x = 5;

其次,函数 five()没有参数、定义了返回值的类型,但函数的主体是一个表达式 5,它作为函数的返回值。

看另外一个例子:

fn main() {let x = plus_one(5);println!("The value of x is: {x}");
}fn plus_one(x: i32) -> i32 {x + 1
}

函数 plus_one() 有一个 i32 类型的参数 x,且其返回值为 i32 类型。假设将函数 plus_one() 定义修改如下:

fn plus_one(x: i32) -> i32 {x + 1; // 多了一个 分号
}

将产生编译错误:

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: remove this semicolon to return this valueFor more information about this error, try `rustc --explain E0308`.

主要错误消息 mismatched types 揭示了此代码的核心问题。函数 plus_one() 的定义说明它将返回一个 i32,但语句不会计算出一个值,该值由 () 表示,即 unit 。因此,不会返回任何内容,这与函数定义相矛盾并导致错误。在此输出中,Rust 提供了一条消息,可能有助于纠正此问题:它建议删除分号,这将修复错误。

3.4 注释

Rust 中的注释

// hello, world// So we’re doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain what’s going on.

3.5 控制流

3.5.1 if 表达式

看一个例子:

fn main() {let number = 3;if number < 5 {println!("condition was true");} else {println!("condition was false");}
}

Rustif 表达式和多数语言(如 C)不一样,它不带小括号。另外,else 表达式也不是必须的

Rustif 表达式 要是一个布尔值,如下代码将导致编译错误,因为变量 number 是一个数字:

fn main() {let number = 3;if number {println!("number was three");}
}
error[E0308]: mismatched types--> src/main.rs:4:8|
4 |     if number {|        ^^^^^^ expected `bool`, found integer

修改代码成如下,将修正编译错误:

fn main() {let number = 3;if number == 3 {println!("number was three");}
}

再看一下 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");}
}
3.5.1.1 在 let 语句中使用 if

因为 if 是一个表达式,我们可以在 let 语句的右侧使用它,来将结果分配给一个变量。如:

fn main() {let condition = true;let number = if condition { 5 } else { 6 };println!("The value of number is: {number}");
}

请记住,代码块的计算结果为其中的最后一个表达式,而数字本身也是表达式。在这种情况下,整个 if 表达式的值取决于执行的代码块。这意味着有可能成为 if 的每个分支的结果的值必须为同一类型;如果类型不匹配,将遇到编译错误,如下例所示:

fn main() {let condition = true;let number = if condition { 5 } else { "six" };println!("The value of number is: {number}");
}
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

if 中的表达式计算结果为整数,else 中的表达式计算结果为字符串。这是行不通的,因为变量必须只有一个类型,而 Rust 需要在编译时明确知道 number 变量是什么类型。知道 number 的类型,可以让编译器验证该类型在我们使用 number 的任何地方都有效。如果 number 的类型仅在运行时确定,Rust 将无法做到这一点;如果编译器必须跟踪任何变量的多个假设类型,则编译器将更复杂,并且对代码可靠性的保证将会更少。

3.5.2 循环

多次执行一个代码块通常很有用,对于此任务,Rust 提供了多种循环,这些循环将遍历循环体内部的代码直到结束,然后立即从头开始。

Rust 有三种类型的循环:loop、while、for,让我们逐一尝试。

3.5.2.1 loop 循环

loop 关键字告诉 Rust 永远一遍又一遍地执行一段代码,或者直到你明确告诉它停止。如:

fn main() {loop {println!("again!");}
}

这个循环永远不会退出,除非按下 Ctrl + C 或类似操作。幸运的是,Rust 还提供了一种使用代码跳出循环的方法,可以在循环中放置 break 关键字,以告知程序何时停止执行循环。另外,continue 关键字将导致进入下一轮循环。

3.5.2.1.1 从 loop 循环返回值

循环的用途之一是重试你知道可能会失败的操作,例如检查线程是否已完成其工作。你可能还需要将该操作的结果从循环中传递给代码的其余部分。为此,你可以在用于停止循环的 break 表达式之后添加要返回的值;该值将从循环中返回,以便你可以使用它,如下所示:

fn main() {let mut counter = 0;let result = loop {counter += 1;if counter == 10 {break counter * 2; // 从 loop 循环返回值}};println!("The result is {result}");
}

也可以从内部循环返回,break 仅退出当前循环,而 return 始终退出当前函数。

3.5.2.1.2 loop 标签以消除多个 loop 之间的歧义

如果 loop 循环有多重,则 breakcontinue 将应用于最里层 loop。你可以选择在循环上指定一个 loop 标签,然后和 breakcontinue 一起使用,以指定它们应用于某个用 loop 标签标记的循环,而不是最内层的循环。loop 标签必须以单引号开头。下面是一个包含两个嵌套循环的示例:

fn main() {let mut count = 0;'counting_up: loop {println!("count = {count}");let mut remaining = 10;loop {println!("remaining = {remaining}");if remaining == 9 {break;}if count == 2 {break 'counting_up;}remaining -= 1;}count += 1;}println!("End count = {count}");
}

外部循环的标签为 'counting_up,它将从 0 到 2 递增;没有标签的内循环从 10 到 9 倒计数。未指定标签的第一个 break 将仅退出内部循环break 'counting_up; 语句将退出外部循环。编译运行代码,结果如下:

count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
3.5.2.2 while 循环

程序通常需要评估循环中的条件:当条件为 true 时,循环运行;当条件不再为 true 时,程序调用 break 并停止循环。可以使用 loop、if、else 和 break 的组合来实现这样的行为。然而,这种模式非常普遍,以至于 Rust 有一个内置的语言结构,称为 while 循环。示例如下:

fn main() {let mut number = 3;while number != 0 {println!("{number}!");number -= 1;}println!("LIFTOFF!!!");
}

这个结构消除了如果你使用 loop、if、else 和 break 所必需的大量嵌套,它更清晰。当条件的计算结果为 true 时,代码将运行;否则,它将退出循环。

3.5.2.3 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;}
}

但是,这种方法容易出错。如果索引值或循环测试条件不正确,可能会导致程序 panic。例如,如果您将 a 数组的定义更改为具有 4 个元素,但忘记将条件更新为 while index < 4,则代码将 panic它也很慢,因为编译器会添加运行时代码,以便在循环的每次迭代中执行索引是否在数组边界内的条件检查

作为更简洁的替代方法,可以使用 for 循环并为集合中的每个元素执行一些代码。如:

fn main() {let a = [10, 20, 30, 40, 50];for element in a {println!("the value is: {element}");}
}

当我们运行这段代码时,我们将看到与 while 示例中相同的输出。更重要的是,我们现在提高了代码的安全性,并消除了因超出数组末尾、或没有遍历所有元素而导致错误的可能性。

使用 for 循环,如果你更改了数组的长度,你仍然不需要更改 for 循环的代码。

for 循环的安全性和简洁性使它们成为 Rust 中最常用的循环结构。即使在你想运行一些代码一定次数的情况下,比如 while 循环的倒计数示例,大多数 Rust 编程人员也会使用 for 循环。执行此操作的方法是使用标准库提供的 Range,它按顺序生成所有数字,从一个数字开始,在另一个数字之前结束。

以下是使用 for 循环和另一种我们尚未涉及的方法 rev 来反转范围的倒计数示例:

fn main() {for number in (1..4).rev() { // (1..4).rev() 反转 (1,2,3) 为 (3,2,1)println!("{number}!");}println!("LIFTOFF!!!");
}

运行代码,得到如下输出:

3!
2!
1!
LIFTOFF!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/53195.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目拆解:短视频冷门赛道—ai绘画+温馨小屋,引流变现全攻略

在这个快节奏的时代&#xff0c;工作、学习、家庭的重担仿佛三座大山&#xff0c;让人喘不过气&#xff0c;心情时常跌入谷底。就像蜗牛遇到威胁会缩进壳里&#xff0c;我们也会在疲惫和忧虑时&#xff0c;渴望一个属于自己的温暖小窝&#xff0c;来安放疲惫的心灵。而自媒体平…

Flink 1.14.* Flink窗口创建和窗口计算源码

解析Flink如何创建的窗口&#xff0c;和以聚合函数为例&#xff0c;窗口如何计算聚合函数 一、构建不同窗口的build类1、全局窗口2、创建按键分流后的窗口 二、在使用窗口处理数据流时&#xff0c;不同窗口创建的都是窗口算子WindowOperator1、聚合函数实现2、创建全局窗口(入参…

SpringFrameWork学习笔记

本笔记基于【尚硅谷新版SSM框架全套视频教程&#xff0c;Spring6SpringBoot3最新SSM企业级开发】https://www.bilibili.com/video/BV1AP411s7D7?vd_sourcea91dafe0f846ad7bd19625e392cf76d8 总结 资料获取网址&#xff1a;https://www.wolai.com/v5Kuct5ZtPeVBk4NBUGBWF 技术…

Java项目: 基于SpringBoot+mysql房产销售系统 (含源码+数据库+开题报告+答辩PPT+毕业论文)

一、项目简介 本项目是一套基于SpringBootmysql房产销售系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功能齐…

数学基础 -- 线性代数之LU分解

LU分解 LU分解&#xff08;LU Decomposition&#xff09;是线性代数中非常重要的一种矩阵分解方法。它将一个方阵分解为一个下三角矩阵&#xff08;L矩阵&#xff09;和一个上三角矩阵&#xff08;U矩阵&#xff09;的乘积。在数值线性代数中&#xff0c;LU分解广泛用于求解线…

Halcon基于灰度值的模板匹配

Halcon基于灰度值的模板匹配 基于灰度值的模板匹配是最经典的模板匹配算法&#xff0c;也是最早提出来的模板匹配算法。这种算法的根本思想是&#xff0c;计算模板图像与检测图像之间的像素灰度差值的绝对值总和&#xff08;SAD方法&#xff09;或者平方差总和&#xff08;SSD…

Spring解析

目录 容器与 bean 1) 容器接口 演示1 - BeanFactory 与 ApplicationContext 的区别 代码参考 收获&#x1f4a1; 演示2 - 国际化 2) 容器实现 演示1 - DefaultListableBeanFactory 代码参考 收获&#x1f4a1; 演示2 - 常见 ApplicationContext 实现 代码参考 收获…

ico格式怎么转换?5个软件让你轻松转换文件格式

ico格式怎么转换&#xff1f;5个软件让你轻松转换文件格式 ICO格式是常用于网站图标和应用程序图标的文件格式&#xff0c;虽然它很常见&#xff0c;但并非所有图像编辑软件都支持直接保存为ICO格式。如果你需要将其他格式的图片&#xff08;如PNG、JPG等&#xff09;转换为IC…

读书学习笔记入门 # Datawhale X 李宏毅苹果书 AI夏令营

文章目录 学习目标&#xff1a;学习内容&#xff1a;Task 1 通过案例了解机器学习机器学习&#xff08;Machine Learning&#xff0c;ML&#xff09;和深度学习&#xff08;Deep Learning&#xff0c;DL&#xff09;的基本概念什么是回归&#xff08;regression&#xff09;什么…

深入解析Linux轻量级进程:线程的概念、原理、优缺点及其与进程的关系与区别

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 &#x1f4da;Linux线程&#x1f4d5;什么是线程*可以使用多进程去并发的执行一个进程的代码&#xff0c;那为什么要由线程呢&#x…

基于CloudflareSpeedTest项目实现git clone加速

1.网络测速 「自选优选 IP」测试 Cloudflare CDN 延迟和速度&#xff0c;获取最快 IP 更多内容参考项目&#xff1a;https://github.com/XIU2/CloudflareSpeedTest 国外很多网站都在使用 Cloudflare CDN&#xff0c;但分配给中国内地访客的 IP 并不友好&#xff08;延迟高、丢…

Pixelmator Pro for Mac 专业图像处理软件【媲美PS的修图软件】

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功 三、运行测试安装完成&#xff01;&#xff01;&#xff01; 效果 一、下载软件 下载软件…

【STM32+HAL库】---- 通用定时器输入捕获PWM信号

硬件开发板&#xff1a;STM32G0B1RET6 软件平台&#xff1a;cubemaxkeilVScode1 新建cubemax工程 1.1 配置系统时钟RCC 1.2 配置定时器 1.2.1 配置输入捕获 选择通用定时器TIM2-Channel 1为输入捕获引脚&#xff0c;对应IO口是PA0,时钟源选择内部时钟源Internal clock,工作模…

Unity实战案例 2D小游戏HappyGlass(模拟水珠)

本案例素材和教程都来自Siki学院&#xff0c;十分感谢教程中的老师 本文仅作学习笔记分享交流&#xff0c;不作任何商业用途 预制体 在这个小案例中&#xff0c;水可以做成圆形但是带碰撞体&#xff0c;碰撞体比图形小一圈&#xff0c;顺便加上Trail renderer组件 材质 将碰撞…

PIL库图像压缩教程

图像压缩教程 本教程展示如何使用Python中的PIL&#xff08;Pillow&#xff09;库进行图像压缩。详细介绍如何加载图像、压缩图像&#xff0c;以及保存压缩后的图像。 步骤包括&#xff1a; 安装所需库加载图像压缩图像保存压缩后的图像 确保环境中安装了Pillow库。如果没有…

Win11 / Win10 系统极化工具,降低游戏延迟效果明显

Win11 / Win10 系统优化工具,降低游戏延迟效果明显 Windows 系统优化就是精简系统一些功能组件、对一些系统功能进行设置等&#xff0c;这样可以减少不必要的硬件资源占用。 全面的系统优化功能外&#xff0c;据不少网友表示通过优化后 CS GO 游戏降低输入延迟效果明显。 免费…

shell脚本示例(用于学习shell语法)

#!/bin/bash # 定义变量 # 设置x264的安装位置 PREFIX"./x264build" # 交叉编译器bin路径,使用aarch64-linux-gnu-gcc -v命令查看 TOOLCHAIN_DIR"/opt/gcc-aarch64-linux-gnu/bin" # 工具链前缀&#xff0c;用于指定交叉编译工具的路径 TOOLCHAIN"…

沃飞长空联合极氪亮相2024世界动力电池大会

9月1日至2日&#xff0c;2024世界动力电池大会在四川宜宾举办&#xff0c;沃飞长空与同属吉利控股集团旗下的新时代豪华科技品牌极氪汽车一同亮相。 现场&#xff0c;双方携手展出了AE200电动垂直起降航空器、极氪009光辉版、极氪001&#xff0c;以及极氪能源、金砖电池、威睿…

docker可用镜像源

{ “debug”:true, “registry-mirrors”: [ “https://hub-mirror.c.163.com”, “https://dockerproxy.com”, “https://mirror.baidubce.com”, “https://ccr.ccs.tencentyun.com”, “https://docker.m.daocloud.io” ] } sudo systemctl daemon-reload sudo systemctl…

2024.09.03【代码实现L1】|使用AI助手0编程基础写脚本

【代码实现L1】| 生信小白0编程基础编写统计脚本 文章目录 【代码实现L1】| 生信小白0编程基础编写统计脚本为什么需要AI助手&#xff1f;如何向AI助手提供指令&#xff1f;示例&#xff1a;编写统计脚本AI助手生成的代码总结 出品| 木青生信大模型 作者| 穆易青、kimi 最近工作…