当前位置: 首页 > news >正文

dede手机网站模板制作广州安全教育平台下载

dede手机网站模板制作,广州安全教育平台下载,广州一网通办注册公司流程,源码时代培训机构上一篇#xff1a;02-编程猜谜游戏 本章介绍几乎所有编程语言中都会出现的概念#xff0c;以及它们在 Rust 中的工作原理。许多编程语言的核心都有许多共同点。本章介绍的概念都不是 Rust 独有的#xff0c;但我们会在 Rust 的上下文中讨论这些概念#xff0c;并解释使用这…上一篇02-编程猜谜游戏 本章介绍几乎所有编程语言中都会出现的概念以及它们在 Rust 中的工作原理。许多编程语言的核心都有许多共同点。本章介绍的概念都不是 Rust 独有的但我们会在 Rust 的上下文中讨论这些概念并解释使用这些概念的惯例。 具体来说将学习变量、基本类型、函数、注释和控制流。每个 Rust 程序中都会有这些基础知识及早学习这些知识将为你的起步打下坚实的基础。 Rust 语言与其他语言一样有一组关键字仅供该语言使用。请记住不能将这些关键字用作变量或函数的名称。大多数关键字都有特殊含义你将在 Rust 程序中使用它们完成各种任务少数关键字目前没有相关功能但被保留用于将来可能添加到 Rust 中的功能。 1. 变量与可变性 默认情况下变量是不可变的。这是 Rust 给你的众多提示之一让你在编写代码时充分利用 Rust 提供的安全性和易并发性。不过你仍然可以选择让变量可变。让我们来探讨一下 Rust 如何以及为什么鼓励你偏爱不可变性以及为什么有时你可能会想选择放弃。 如果变量是不可变的那么一旦某个值与名称绑定就无法更改该值。为了说明这一点请使用 cargo new variables 在你的项目目录中生成一个名为 variables 的新项目。然后在新的变量目录下打开 src/main.rs将其代码替换为以下代码暂时还无法编译 fn main() {let x 5;println!(The value of x is: {x});x 6;println!(The value of x is: {x}); }保存并使用 cargo run 运行程序。你应该会收到一条关于不可变性错误的错误信息如输出所示 cargo.exe runCompiling variables v0.1.0 (D:\rustProj\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 variableFor more information about this error, try rustc --explain E0384. error: could not compile variables (bin variables) due to previous error 这个示例展示了编译器如何帮助你发现程序中的错误。编译器错误可能会令人沮丧但实际上它们只意味着你的程序还不能安全地完成你想要它做的事情它们并不意味着你不是一个优秀的程序员经验丰富的 Rustaceans 仍然会遇到编译器错误。 你收到错误信息 cannot assign twice to immutable variable x 是因为你试图给不可变的 x 变量赋第二个值。 当我们试图更改指定为不可变的值时编译时出错是很重要的因为这种情况可能会导致错误。如果我们代码的一部分假定某个值永远不会改变而另一部分代码却改变了该值那么前一部分代码就有可能无法完成其设计目标。这种错误的原因可能很难事后追踪尤其是当第二部分代码只是偶尔改变值时。Rust 编译器保证当您声明某个值不会改变时它确实不会改变因此您不必自己跟踪它。这样您的代码就更容易推理了。 不过可变性可能非常有用可以让代码编写更方便。虽然变量在默认情况下是不可变的但也可以通过在变量名前添加 mut 来使其可变。添加 mut 还可以向代码的未来读者传达意图表明代码的其他部分将改变该变量的值。 当使用 mut 时我们可以将绑定到 x 的值从 5 改为 6 。最终是否使用可变性取决于您自己取决于您认为在特定情况下什么是最明确的。 2. 常量 与不可变变量一样常量也是与名称绑定且不允许更改的值但常量和变量之间有一些区别。 首先不允许在常量中使用 mut 。常量不仅默认不可变而且始终不可变。您可以使用 const 关键字而不是 let 关键字来声明常量并且必须注释值的类型。 常量可以在任何作用域包括全局作用域中声明因此对于代码中许多部分都需要知道的值来说常量非常有用。 最后一个区别是常量只能设置为一个常量表达式而不能设置为只能在运行时计算的值的结果。 下面是一个常量声明的示例 const THERR_HOURS_IN_SECONDS: u32 60 * 60 *3; 常量的名称是 THREE_HOURS_IN_SECONDS 其值设置为 10800。Rust 的常量命名规则是使用全大写字母字与字之间使用下划线。编译器可以在编译时评估有限的操作集这让我们可以选择以更容易理解和验证的方式写出这个值而不是将这个常量设置为 10,800 值。 常量在程序运行的整个过程中在常量声明的作用域内都是有效的。常量的这一特性对于程序中多个部分可能需要了解此值非常有用例如游戏中允许任何玩家获得的最大点数或光速。 将整个程序中使用的硬编码值命名为常量有助于向未来的代码维护者传达该值的含义。此外如果将来需要更新硬编码值只需更改代码中的一处即可。 3. 阴影 正如在第 2 章的猜谜游戏教程中所看到的你可以声明一个与前一个变量同名的新变量。Rustaceans 说第一个变量会被第二个变量遮挡这意味着当你使用变量名时编译器会看到第二个变量。实际上第二个变量会覆盖第一个变量将变量名的任何使用都带到自己身上直到它自己被阴影覆盖或作用域结束。我们可以通过使用相同的变量名和重复使用 let 关键字来对变量进行阴影处理如下所示 fn main() {let x: i32 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}); }该程序首先将 x 与 5 的值绑定。然后它通过重复 let x 来创建一个新变量 x 将原来的值加上 1 这样 x 的值就是 6 。然后在用大括号创建的内部作用域中第三个 let 语句也会对 x 进行阴影处理并创建一个新变量将之前的值乘以 2 使 x 的值为 12 。当该作用域结束时内部阴影结束 x 返回到 6 当我们运行这个程序时它将输出如下内容 cargo.exe runCompiling variables v0.1.0 (D:\rustProj\variables)Finished dev [unoptimized debuginfo] target(s) in 0.58sRunning target\debug\variables.exe The value of x in the inner scope is:12 The value of x is:6 Shadowing 与将变量标记为 mut 不同因为如果我们不小心在没有使用 let 关键字的情况下将变量重新赋值就会在编译时出错。通过使用 let 我们可以对一个值执行一些变换但在这些变换完成后变量将不可变。 mut 和阴影之间的另一个区别是当我们再次使用 let 关键字时实际上是创建了一个新变量因此我们可以改变值的类型但可以重复使用相同的名称。例如我们的程序要求用户通过输入空格字符来显示他们希望在某些文本之间输入多少个空格然后我们希望将输入值存储为一个数字 let spaces ; let spaces spaces.len(); 第一个 spaces 变量是字符串类型第二个 spaces 变量是数字类型。因此我们不必使用不同的名称如 spaces_str 和 spaces_num 相反我们可以重复使用更简单的 spaces 名称。但是如果我们尝试使用 mut 就会出现编译时错误 fn main() {let cat BlueCat;let cat Muppet;let mut space ;space space.len(); }错误提示我们不允许更改变量的类型 cargo.exe buildCompiling variables v0.1.0 (D:\rustProj\variables) error[E0308]: mismatched types-- src\main.rs:6:13| 5 | let mut space ;| ---- expected due to this value 6 | space space.len();| ^^^^^^^^^^^ expected str, found usize| help: try removing the method call| 6 - space space.len(); 6 space space;|For more information about this error, try rustc --explain E0308. error: could not compile variables (bin variables) due to previous error 既然我们已经了解了变量的工作原理那么让我们来看看变量可以有哪些数据类型。 4. 数据类型 Rust 中的每个值都有特定的数据类型这就告诉了 Rust 所指定的数据类型以便它知道如何处理这些数据。我们将研究两个数据类型子集标量和复合。 Rust 是一种静态类型语言这意味着它必须在编译时知道所有变量的类型。编译器通常可以根据值和使用方式推断出我们想要使用的类型。 在可能存在多种类型的情况下例如我们在第 2 章 将猜测的数字与秘密数字进行比较 一节中使用 parse 将 String 转换为数字类型时我们必须添加一个类型注解就像下面这样 let guess: u32 42.parse().expect(Not a number!); 如果我们不添加前面代码中显示的 : u32 类型注解Rust 将显示如下错误这意味着编译器需要我们提供更多信息才能知道我们要使用哪种类型 cargo.exe buildCompiling variables v0.1.0 (D:\rustProj\variables) 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 variables (bin variables) due to previous error 你会看到其他数据类型有不同的类型注解。 4.1 标量类型 标量类型表示单个值。Rust 有四种主要的标量类型整数、浮点数、布尔型和字符型。您可能在其他编程语言中见过这些类型。让我们来了解一下它们在 Rust 中是如何工作的。 4.1.1 整数类型 整数是没有小数成分的数字。我们在第 2 章中使用了一种整数类型即 u32 类型。该类型声明表示与之关联的值应该是一个无符号整数有符号整数类型以 i 而不是 u 开头占用 32 位空间。表 3-1 列出了 Rust 中的内置整数类型。我们可以使用其中任何一种变量来声明整数值的类型。 Integer Types in Rust 每个变量都可以是有符号或无符号的并有明确的大小。有符号和无符号指的是数字是否有可能是负数换句话说数字是否需要带有符号有符号或者数字是否只能是正数因此可以不带符号无符号。这就像在纸上书写数字当符号很重要时数字会用加号或减号表示然而当可以肯定数字是正数时数字就不带符号。有符号的数字使用二进制表示法存储。 每个带符号的变量可以存储从到 的数字其中 n 是该变体使用的比特数。因此一个 i8 可以存储 -() 到的数字相当于 -128 到 127。无符号变量可以存储 0 至的数字因此 u8 可以存储 0 至 的数字相当于 0 至 255。 此外 isize 和 usize 类型取决于程序运行的计算机体系结构在表中用 arch 表示如果是 64 位架构则为 64 位如果是 32 位架构则为 32 位。 可以用下表的任何形式编写整数字面量。需要注意的是可以是多种数值类型的数字字面量允许使用类型后缀如 57u8 来指定类型。 数字字面量也可以使用_作为视觉分隔符使数字更容易阅读如 1_000 其值与指定的 1000 相同。 Integer Literals in Rust 那么如何知道使用哪种整数类型呢如果你不确定Rust 的默认值通常是个很好的开始整数类型默认为 i32 。使用 isize 或 usize 的主要情况是索引某种集合。 整型溢出     假设你有一个类型为 u8 的变量它可以保存 0 到 255 之间的值。如果您尝试将该变量更改为该范围之外的值例如 256就会发生整数溢出这可能导致两种行为之一。在调试模式下编译时Rust 会对整数溢出进行检查如果出现这种情况程序会在运行时panic 。     当你使用 --release 标志在 release 模式下编译时Rust 不会对导致恐慌的整数溢出进行检查。相反如果发生溢出Rust 会执行二的补码。简而言之大于类型所能容纳的最大值的值会 缠绕 到类型所能容纳的最小值。在 u8 的情况下值 256 会变成 0值 257 会变成 1以此类推。程序不会慌乱但变量的值可能不是你期望的值。依赖整数溢出的包装行为被视为错误。  要明确处理溢出的可能性可以使用标准库为原始数值类型提供的这些方法系列         ① 使用 wrapping_* 方法对所有模式进行包裹如 wrapping_add         ② 如果 checked_* 方法出现溢出则返回 None 值。         ③ 返回值和一个布尔值表示 overflowing_* 方法是否存在溢出。         ④ 使用 saturating_* 方法对数值的最小值或最大值进行饱和处理。 4.1.2 浮点类型 Rust 还为浮点数带有小数点的数提供了两种原始类型。Rust 的浮点类型是 f32 和 f64 大小分别为 32 位和 64 位。默认类型是 f64 因为在现代 CPU 上它的速度与 f32 大致相同但精度更高。所有浮点类型都是带符号的。 下面的示例展示了浮点数的实际应用 fn main() {let x 2.0; // f64let y: f32 3.0; // f32 } 浮点数根据 IEEE-754 标准表示。 f32 类型是单精度浮点数 f64 是双精度浮点数。 4.1.3 数字运算 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; // Result in -1// remainderlet remainder 43 % 5;print!(sum:{sum}, difference:{difference}, product:{product}, );println!(quotient:{quotient}, truncated:{truncated}, remainder:{remainder}); }4.4.4 布尔类型 与大多数其他编程语言一样Rust 中的布尔类型有两种可能的值 true 和 false 。布尔类型的大小为一个字节。Rust 中的布尔类型使用 bool 指定。例如 fn main() {let t true;let f: bool false; // with explicit type annotation } 使用布尔值的主要方式是通过条件例如 if 表达式 4.4.5 字符类型 Rust 的 char 类型是该语言最原始的字母类型。下面是一些声明 char 值的示例 fn main() {let c z;let z: char ℤ; // with explicit type annotationlet heart_eyed_cat ; }请注意我们使用单引号指定 char 字面量而字符串字面量则使用双引号。Rust 的 char 类型大小为 4 个字节表示 Unicode 标量值这意味着它不仅可以表示 ASCII 码还可以表示很多其他字符。重音字母、中文、日文和韩文字符、表情符号和零宽度空格都是 Rust 中有效的 char 值。Unicode 标量值的范围从 U0000 到 UD7FF 从 UE000 到 U10FFFF 。不过字符 在 Unicode 中并不是一个真正的概念因此你对 字符 的直觉可能与 Rust 中的 char 并不一致。 4.4.6 复合类型 复合类型可以将多个值组合成一个类型。Rust 有两种原始的复合类型元组和数组。 4.4.6.1 元组 元组是将多种类型的数值组合成一个复合类型的通用方法。元组有固定的长度一旦声明其大小就不能增大或缩小。 我们通过在括号内写入一个以逗号分隔的值列表来创建一个元组。元组中的每个位置都有一个类型元组中不同值的类型不一定相同。我们在本例中添加了可选的类型注解 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 值。 4.4.6.2 数组 另一种拥有多个值集合的方法是使用数组。与元组不同数组的每个元素都必须具有相同的类型。与其他一些语言中的数组不同Rust 中的数组有固定的长度。 我们将数组中的值写成方括号内用逗号分隔的列表 fn main() {let a [1, 2, 3, 4, 5]; } 当你希望在栈而不是堆上分配数据时或者当你希望确保总是有固定数量的元素时数组就非常有用了。不过数组不如vector类型灵活。vector是标准库提供的一种类似的集合类型其大小可以增大或缩小。如果你不确定是使用数组还是vector那么很可能应该使用vector。 不过当你知道元素的数量不需要改变时数组会更有用。例如如果你要在程序中使用月份名称你可能会使用数组而不是vector因为你知道数组总是包含 12 个元素 let months [January, February, March, April, May, June, July,August, September, October, November, December];在写数组类型时可以用方括号写出每个元素的类型、分号然后写出数组中的元素个数就像这样 let a: [i32; 5] [1, 2, 3, 4, 5];这里 i32 是每个元素的类型。分号后的数字 5 表示数组包含 5 个元素。 您也可以通过指定初始值、分号和方括号中的数组长度来初始化数组使每个元素都包含相同的值如图所示 let a [3; 5];名为 a 的数组将包含 5 元素这些元素的初始值都将设置为 3 。这与 let a [3, 3, 3, 3, 3]; 的写法相同但更为简洁。 4.4.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 。 4.4.6.2.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 运行此代码并输5则会看到如下输出 cargo.exe runFinished dev [unoptimized debuginfo] target(s) in 0.01sRunning target\debug\variables.exe Please enter an array index. 5 thread main panicked at src\main.rs:19:19: index out of bounds: the len is 5 but the index is 5 note: run with RUST_BACKTRACE1 environment variable to display a backtrace error: process didnt exit successfully: target\debug\variables.exe (exit code: 101) 程序在索引操作中使用无效值时出现运行时错误。程序带着错误信息退出并且没有执行最后的 println! 语句。当你尝试使用索引访问元素时Rust 会检查你指定的索引是否小于数组长度。如果索引大于或等于长度Rust 就会慌乱。这种检查必须在运行时进行尤其是在这种情况下因为编译器不可能知道用户稍后运行代码时会输入什么值。 这是 Rust 内存安全原则发挥作用的一个例子。在许多底层语言中这种检查是不存在的当你提供了一个不正确的索引时就会访问到无效的内存。Rust 会立即退出而不是允许访问内存并继续从而防止出现这种错误。 5. 函数 函数在 Rust 代码中非常普遍。您已经看到了该语言中最重要的函数之一 main 函数它是许多程序的入口点。你还见过 fn 关键字它允许你声明新函数。 Rust 代码使用蛇形大小写作为函数和变量名的常规样式其中所有字母都是小写下划线分隔单词。下面是一个包含函数定义示例的程序 fn main() {println!(Hello, world!);another_function(); }fn another_function() {println!(Another function.); } 在 Rust 中定义函数时我们需要输入 fn 然后输入函数名和一组括号。大括号告诉编译器函数体的开始和结束位置。 我们可以调用我们定义的任何函数只需输入其名称并在后面加上一组括号即可。由于 another_function 是在程序中定义的因此可以从 main 函数内部调用它。请注意我们在源代码中的 main 函数之后定义了 another_function 我们也可以在之前定义它。Rust 并不在乎你在哪里定义函数只在乎函数是否定义在调用者可以看到的作用域中。 这点与C/C语言不同 5.1 函数参数 我们可以为函数定义参数这些参数是特殊变量是函数签名的一部分。当函数有参数时可以为这些参数提供具体的值。从技术上讲这些具体值被称为参数但在闲聊中人们倾向于交替使用参数parameter和参数argument这两个词来指代函数定义中的变量或调用函数时传入的具体值。 parameter形参 argument: 实参 在 another_function 这个版本中我们添加了一个参数 fn main() {another_function(3.14); }fn another_function(x: f64) {println!(The value of x:{x}); }请尝试运行该程序您将得到以下输出结果 cargo.exe runCompiling another_function v0.1.0 (D:\rustProj\another_function)Finished dev [unoptimized debuginfo] target(s) in 0.65sRunning target\debug\another_function.exe The value of x:3.14 another_function 的声明有一个名为 x 的参数。 x 的类型指定为 f64。当我们将 3.14 传递给 another_function 时 println! 宏会将 3.14放在格式字符串中包含 x 的那对大括号的位置。 在函数签名中必须声明每个参数的类型。这是 Rust 设计中的一个深思熟虑的决定要求在函数定义中进行类型注解意味着编译器几乎不需要在代码的其他地方使用类型注解来确定你所指的是什么类型。如果编译器知道函数所期望的类型它还能给出更有用的错误信息。 定义多个参数时请使用逗号分隔参数声明如下所示 fn main() {another_function(3.14, T); }fn another_function(x: f64, y: char) {println!(The value of x:{x}, y:{y}); }让我们试着运行这段代码 cargo.exe runCompiling another_function v0.1.0 (D:\rustProj\another_function)Finished dev [unoptimized debuginfo] target(s) in 0.58sRunning target\debug\another_function.exe The value of x:3.14, y:T 5.2 语句与表达式 函数体由一系列语句组成可选择以表达式结尾。到目前为止我们所涉及的函数还没有包含结束表达式但我们已经看到表达式作为语句的一部分。因为 Rust 是一种基于表达式的语言所以理解这一点很重要。其他语言没有相同的区别因此让我们来看看什么是语句和表达式以及它们的区别如何影响函数的主体。 ①. 语句是执行某些操作但不返回值的指令 ②. 表达式会求得一个结果值 表达式有返回值语句没有返回值 实际上我们已经使用过语句和表达式。使用 let 关键字创建变量并赋值就是语句。 fn main() {let y 6; } 函数定义也是语句上面整个例子本身就是一个语句。 语句不返回值。因此不能像下面的代码那样将 let 语句赋值给另一个变量否则会出错 fn main() {let x (let y 6); } 运行此程序时会出现如下错误 cargo.exe buildCompiling another_function v0.1.0 (D:\rustProj\another_function) error: expected expression, found let statement-- src\main.rs:2:14| 2 | let x (let y 8);| ^^^| note: only supported directly in conditions of if and while expressionswarning: unnecessary parentheses around assigned value-- src\main.rs:2:13| 2 | let x (let y 8);| ^ ^| note: #[warn(unused_parens)] on by default help: remove these parentheses| 2 - let x (let y 8); 2 let x let y 8;|warning: another_function (bin another_function) generated 1 warning error: could not compile another_function (bin another_function) due to previous error; 1 warning emitted 表达式会求值是 Rust 代码的主要组成部分。考虑一个数学运算例如 5 6 它是一个求值为 11 的表达式。表达式可以是语句的一部分上述代码中语句 let y 6; 中的 6 是一个表达式其值为 6 。调用函数是一个表达式。调用宏也是一个表达式。例如用大括号创建的新作用域块就是一个表达式 fn main() {let y {let x 3;x 1};println!(The value of y:{y}); }这种做法 {let x 3;x 1 } 是一个代码块在本例中其值为 4 。作为 let 语句的一部分该值被绑定到 y 。请注意 x 1 行的末尾没有分号这与您目前看到的大多数行不同。表达式不包括结尾的分号。如果在表达式末尾添加分号表达式就会变成语句并且不会返回值。在接下来学习函数返回值和表达式时请牢记这一点。 5.3 带返回值的函数 函数可以向调用它的代码返回值。我们不为返回值命名但必须在箭头 ( - ) 后声明其类型。在 Rust 中函数的返回值与函数体块中最终表达式的值同义。您可以使用 return 关键字并指定一个值来提前返回函数但大多数函数都是隐式返回最后一个表达式。下面是一个返回值的函数示例 fn five() - i32 {5 }fn main() {let x five();println!(The value of x:{x}); }five 函数中没有函数调用、宏甚至没有 let 语句只有数字 5 本身。在 Rust 中这是一个完全有效的函数。请注意函数的返回类型也被指定为 - i32 。试着运行这段代码输出结果应该是这样的 cargo.exe runCompiling another_function v0.1.0 (D:\rustProj\another_function)Finished dev [unoptimized debuginfo] target(s) in 0.60sRunning target\debug\another_function.exe The value of x:5 five 中的 5 是函数的返回值这就是返回类型为 i32 的原因。让我们来详细研究一下。有两个重要的部分首先 let x five(); 这一行表明我们正在使用函数的返回值来初始化一个变量。因为函数 five 的返回值是 5 所以该行与下面的内容相同 let x 5; 其次 five 函数没有参数并定义了返回值的类型但函数体是一个没有分号的孤零零的 5 因为它是一个表达式我们要返回它的值。 我们再来看一个例子 fn plue_one(x: i32) - i32 {x 1 }fn main() {let x plue_one(5);println!(The value of x:{x}); }运行这段代码将打印 The value of x is: 6 。但如果我们在包含 x 1 的行尾加上分号将其从表达式改为语句就会出现错误 cargo.exe runCompiling another_function v0.1.0 (D:\rustProj\another_function) error[E0308]: mismatched types-- src\main.rs:1:24| 1 | fn plue_one(x: i32) - i32 {| -------- ^^^ expected i32, found ()| || implicitly returns () as its body has no tail or return expression 2 | x 1;| - help: remove this semicolon to return this valueFor more information about this error, try rustc --explain E0308. error: could not compile another_function (bin another_function) due to previous error 主要错误信息 mismatched types 揭示了这段代码的核心问题。函数 plus_one 的定义说它将返回一个 i32 但语句并没有求值而值是由单元类型 () 表示的。因此不会返回任何值这与函数定义相矛盾并导致错误。在该输出中Rust 提供了一条可能有助于纠正该问题的信息它建议删除分号这样就可以修复该错误。 6. 注释 所有程序员都努力使自己的代码易于理解但有时也需要额外的解释。在这种情况下程序员会在源代码中留下注释编译器会忽略这些注释但阅读源代码的人可能会觉得有用。 这里有一个简单的评论 // hello world! 在 Rust 中惯用的注释样式是以两个斜线开始注释注释一直持续到行尾。对于超出一行的注释需要在每一行都包含 // 就像这样 // 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.注释也可以放在包含代码的行尾 fn main() {let lucky_number 8; // I’m feeling lucky today } 但你更经常看到的是以这种格式使用的注释即在注释代码的上方另起一行 fn main() {// I’m feeling lucky todaylet lucky_number 8; } Rust 还有另一种注释即文档注释在后面的章节中再介绍。 7. 控制流 根据条件是否 true 运行某些代码以及在条件 true 时重复运行某些代码的能力是大多数编程语言的基本构件。让您控制 Rust 代码执行流的最常见结构是 if 表达式和循环。 7.1 if表达式 if 表达式允许您根据条件分支代码。您可以提供一个条件然后声明如果满足此条件则运行此代码块。如果不满足条件则不运行此代码块。 在项目目录下新建一个名为branch的项目以探索 if 表达式 fn main() {let number 3;if number 5 {println!(condition was true);} else {println!(condition was false);} }所有 if 表达式都以关键字 if 开头后面跟一个条件。在本例中条件检查变量 number 的值是否小于 5。我们将条件为 true 时要执行的代码块紧接在大括号内的条件之后。与 if 表达式中的条件相关的代码块有时被称为 arms就像我们在第 2 章 将猜测与秘密数字进行比较 一节中讨论的 match 表达式中的 arms 一样。 我们还可以选择加入 else 表达式我们在这里选择了这样做以便在条件评估结果为 false 时为程序提供另一个要执行的代码块。如果不提供 else 表达式而条件是 false 程序将跳过 if 代码块继续执行下一段代码。 请尝试运行这段代码您将看到以下输出 cargo.exe runCompiling branch v0.1.0 (D:\rustProj\branch)Finished dev [unoptimized debuginfo] target(s) in 0.69sRunning target\debug\branch.exe condition was true 让我们试着将 number 的值改为能使条件 false 的值看看会发生什么 let number 7; 再次运行程序查看输出结果 cargo.exe runCompiling branch v0.1.0 (D:\rustProj\branch)Finished dev [unoptimized debuginfo] target(s) in 0.60sRunning target\debug\branch.exe condition was false 值得注意的是这段代码中的条件必须是 bool 。如果条件不是 bool 我们就会出错。例如请尝试运行以下代码 fn main() {let number 3;if number {println!(condition was true);} else {println!(condition was false);} }if 条件这次的求值结果是 3 Rust 会抛出一个错误 cargo.exe buildCompiling branch v0.1.0 (D:\rustProj\branch) error[E0308]: mismatched types-- src\main.rs:4:8| 4 | if number {| ^^^^^^ expected bool, found integerFor more information about this error, try rustc --explain E0308. error: could not compile branch (bin branch) due to previous error 该错误表明 Rust 期望得到一个 bool 但得到的却是一个整数。与 Ruby 和 JavaScript 等语言不同Rust 不会自动尝试将非布尔类型转换为布尔类型。您必须明确地将布尔类型作为条件提供给 if 。例如如果我们希望 if 代码块只在一个数字不等于 0 时运行我们可以将 if 表达式改为如下 fn main() {let number 3;if number ! 0 {println!(condition was true);} else {println!(condition was false);} }运行这段代码将打印condition was true 7.2 处理多个条件 else if 通过在 else if 表达式中组合 if 和 else 可以使用多个条件。例如 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);} }该程序有四种可能的运行路径。运行该程序后您将看到以下输出结果 cargo.exe runFinished dev [unoptimized debuginfo] target(s) in 0.00sRunning target\debug\control_flow.exe number is divisible by 3 执行该程序时它会依次检查每个 if 表达式并执行条件求值为 true 的第一个正文。请注意尽管 6 可以被 2 整除但我们并没有看到输出 number is divisible by 2 也没有看到 else 代码块中的 number is not divisible by 4, 3, or 2 文本。这是因为 Rust 只执行了第一个 true 条件的代码块一旦找到一个就不会再检查其余的了。 使用过多的 else if 表达式会使代码变得杂乱无章因此如果使用的表达式超过一个可能需要重构代码。后面会介绍一种强大的 Rust 分支结构称为 match 用于处理这些情况。 7.3 在 let 声明中使用 if 因为 if 是一个表达式所以我们可以在 let 语句的右侧使用它将结果赋值给一个变量如下所示 fn main() {let condition true;let number if condition { 5 } else { 6 };println!(The vaule of number is:{number}); }number 变量将根据 if 表达式的结果绑定到一个值上。运行这段代码看看会发生什么 cargo.exe runCompiling control_flow v0.1.0 (E:\rustProj\control_flow)Finished dev [unoptimized debuginfo] target(s) in 0.32sRunning target\debug\control_flow.exe The vaule of number is:5 请记住代码块对其中的最后一个表达式进行求值而数字本身也是表达式。在这种情况下整个 if 表达式的值取决于执行哪个代码块。这意味着有可能成为 if 各分支结果的值必须是相同的类型在上述代码中 if 分支和 else 分支的结果都是 i32 整数。如果类型不匹配如下面的示例我们就会出错 fn main() {let condition true;let number if condition { 5 } else { 6 };println!(The vaule of number is:{number}); }当我们尝试编译这段代码时会出现错误。 if 和 else 两条手臂的值类型不兼容而 Rust 则准确地指出了程序中的问题所在 cargo.exe runCompiling control_flow v0.1.0 (E:\rustProj\control_flow) error[E0308]: if and else have incompatible types-- src\main.rs:3:44| 3 | let number if condition { 5 } else { 6 };| - ^^^ expected integer, found char| || expected because of thisFor more information about this error, try rustc --explain E0308. error: could not compile control_flow (bin control_flow) due to previous error if 代码块中的表达式求值为整数而 else 代码块中的表达式求值为字符串。这样做是行不通的因为变量必须具有单一类型而 Rust 需要在编译时明确知道 number 变量的类型。知道了 number 的类型编译器就可以在使用 number 的地方验证类型是否有效。如果 number 的类型只能在运行时确定那么 Rust 就无法做到这一点如果编译器必须跟踪任何变量的多种假设类型那么编译器就会变得更加复杂对代码的保证也会减少。 8. 循环 执行一个代码块不止一次通常很有用。为此Rust 提供了多个循环它们会将循环体中的代码执行到底然后立即从头开始。为了尝试使用循环让我们创建一个名为 loops 的新项目。 Rust 有三种循环 loop , while , 和 for 。 8.1 loop循环 loop 关键字会告诉 Rust 永远重复执行一个代码块直到你明确告诉它停止为止。 举例来说将 loops 目录中的 src/main.rs 文件修改为如下所示 fn main() {loop {println!(Hello, world!);} }当我们运行这个程序时会看到 Hello, world!不断重复打印直到我们手动停止程序。大多数终端支持键盘快捷键 ctrl-c用于中断陷入持续循环的程序。 8.1.1 从循环中返回值 loop 的用途之一是重试已知可能会失败的操作例如检查线程是否已完成任务。您可能还需要将该操作的结果从循环中传递给代码的其他部分。为此您可以在用于停止循环的 break 表达式后添加希望返回的值该值将从循环中返回以便您使用如下所示 fn main() {let mut counter 0;let result loop {counter 1;if counter 10 {break counter * 2;}};println!(The result is {result}); }在循环之前我们声明一个名为 counter 的变量并将其初始化为 0 。然后我们声明一个名为 result 的变量用于保存循环返回的值。在循环的每次迭代中我们将 1 添加到 counter 变量中然后检查 counter 是否等于 10 。如果等于我们就使用 break 关键字并将其值改为 counter * 2 。循环结束后我们使用分号结束赋值给 result 的语句。最后我们打印 result 中的值本例中的值为 20 。 8.1.2 在多个循环之间消除歧义的循环标签 如果在循环中存在循环 break 和 continue 适用于此时的最内层循环。您可以选择在循环上指定一个循环标签然后与 break 或 continue 一起使用以指定这些关键字适用于带标签的循环而不是最内层的循环。循环标签必须以单引号开头。下面是一个包含两个嵌套循环的示例 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; 语句将退出外循环。该代码将打印 cargo.exe runCompiling loops v0.1.0 (E:\rustProj\loops)Finished dev [unoptimized debuginfo] target(s) in 0.30sRunning target\debug\loops.exe count 0 remaining 10 remaining 9 count 1 remaining 10 remaining 9 count 2 remaining 10 End count 2 8.2 while循环 程序经常需要在循环中评估一个条件。当条件为 true 时循环运行。当条件不再是 true 时程序会调用 break 停止循环。使用 loop 、 if 、 else 和 break 的组合可以实现类似的行为如果你愿意现在就可以在程序中尝试。不过这种模式非常常见Rust 为此提供了一种内置的语言结构称为 while 循环。下列代码中我们使用 while 循环程序三次每次倒计时然后在循环结束后打印一条信息并退出。 fn main() {let mut number 3;while number ! 0 {println!({number}!);number - 1;}println!(LIFTOFF!!!); } 如果使用 loop , if , else , 和 break 这种结构会省去很多嵌套而且更加清晰。当条件的值为 true 时代码将运行否则将退出循环。 8.3 for循环 您可以选择使用 while 结构对数组等集合的元素进行循环。例如下列代码中的循环打印数组 a 中的每个元素。 fn main() {let a [10, 20, 30, 40, 50];let mut i 0;while i 5 {println!(the value is:{}, a[i]);i 1;} } 在这里代码对数组中的元素进行计数。它从索引 0 开始然后循环直到数组中的最终索引即 index 5 不再是 true 时。运行这段代码将打印数组中的每个元素 cargo.exe runCompiling loops v0.1.0 (E:\rustProj\loops)Finished dev [unoptimized debuginfo] target(s) in 0.29sRunning target\debug\loops.exe the value is:10 the value is:20 the value is:30 the value is:40 the value is:50 所有五个数组值都如期出现在终端中。尽管 index 会在某一时刻达到 5 的值但在尝试从数组中获取第六个值之前循环就停止执行了。 如果我们将while循环中的i 1修改为i则在编译时会报错  cargo.exe build    Compiling loops v0.1.0 (E:\rustProj\loops) error: Rust has no postfix increment operator  -- src\main.rs:7:10   | 7 |         i;   |          ^^ not a valid postfix operator   | help: use 1 instead   | 7 |         i 1;   |           ~~~~ error: could not compile loops (bin loops) due to previous error 也就是说Rust语言不支持类似C/C那样的i/i这样的操作 不过这种方法容易出错如果索引值或测试条件不正确我们可能会导致程序宕机。例如如果将 a 数组的定义改为包含四个元素但忘记将条件更新为 while index 4 代码就会宕机。此外这样做的速度也很慢因为编译器会添加运行时代码在循环的每次迭代中执行索引是否在数组范围内的条件检查。 作为一种更简洁的替代方法您可以使用 for 循环为集合中的每个项目执行一些代码。 for 循环与上述的代码相似。 fn main() {let a [10, 20, 30, 40, 50];for element in a {println!(the value is:{element});} } 运行这段代码后我们将看到与while循环有相同的输出结果。更重要的是我们现在提高了代码的安全性消除了因超出数组末尾或遗漏某些项所可能导致的错误。 使用 for 循环如果改变数组中的数值个数就不需要像while循环那样还需要同步修改其他代码。 for 循环的安全性和简洁性使其成为 Rust 中最常用的循环结构。即使在需要运行一定次数代码的情况下使用 while 循环的示例大多数 Rustaceans 也会使用 for 循环。这样做的方法是使用标准库提供的 Range 它可以按顺序生成从一个数字开始到另一个数字之前结束的所有数字。 下面是使用 for 循环和另一种我们尚未讨论过的方法 rev 来反转范围的倒计时效果 fn main() {for number in (1..4).rev() {println!({number});}println!(LIFTOFF!!!); } 这个代码更好看一些不是吗 下一篇04-了解所有权
http://www.zqtcl.cn/news/672987/

相关文章:

  • asp网站开发 pdf企业展厅设计公司盛世笔特
  • 怎么创建网站 免费的免费开源的网站系统
  • 中山精品网站建设资讯网页设计师就业趋势
  • 网站建设哪家好 万维科技wordpress广告公司模板
  • 如何选择建网站公司网站页面html静态化
  • 建设银行网站入口网页设计培训 周末双休
  • 做企业网站建设的公司为什么企业网站不是开源系统
  • 网站客户端怎么做的做汽车脚垫版的网站
  • 做数学题挣钱的网站广西建筑特种作业证件查询官网
  • 汉字叔叔花了多少钱做网站免费原创视频素材
  • 网站开发提现功能互联网推广工作好做吗
  • 做阿里渠道的销售要有哪些网站网站评论怎么做的
  • 建设中网站如何上传图片深圳营销型网站建设设计公司
  • 建设电商网站需要多少钱家具网页设计素材
  • 物流网站html5模板网站整站开发
  • 网站随机代码网站开发技术试验教程
  • 做翻译 网站吗仿京东电商的网站开发报价
  • 霞山网站建设公司网站开发怎样手机号验证
  • 大型门户网站建设苏州优化网站建设
  • 网站步骤怎么搭建个人网站
  • 荥阳网站建设公司wordpress会员上限
  • 采购需求网站建设呼伦贝尔网站开发
  • 东莞网站建设方案服务极速网站建设定制价格
  • 网站建设费记账福州百度网络推广
  • 中国农村建设网站邵阳房产网
  • 做非法网站网站上海备案查询
  • 网站制作要学哪些北京信管局 网站备案
  • 百度新闻源网站有哪些wordpress怎么配置七牛cdn加速
  • 山东城乡住房建设厅网站wordpress 购物网站主题
  • 石家庄制作网站查网站流量查询工具