锈编程书

图片你好居住者!《 Rust编程语言官方指南》将帮助您创建更快,更可靠的软件。高级人机工程学和低级控件经常相互冲突,但是Rust挑战了这种冲突。



本书的作者是语言开发团队的一部分,这意味着您将直接获得所有信息-从安装语言到创建可靠且可扩展的程序。通过创建函数,选择数据类型和绑定变量,您将继续研究更复杂的概念:



  • 所有权和借款,生命周期和类型。
  • 有保证的软件安全性。
  • 测试,错误处理和有效的重构。
  • 泛型,智能指针,多线程,特征对象和映射。
  • 与内置的包管理器Cargo一起构建,测试,记录代码和管理依赖项。
  • 用于处理Unsafe Rust的高级工具。


您将找到许多代码示例,以及有关创建完整项目以巩固知识的三章:猜测游戏,构建命令行工具和多线程服务器。



这本书是给谁的



我们假设您用另一种编程语言编写了代码,但是我们不假设使用哪种编程语言。我们试图使具有广泛编程技能的人员可以访问此材料。我们不会浪费时间谈论什么是编程。如果您是编程的绝对新手,请先阅读编程介绍。



如何使用这本书
-, , , . , ; .



: . . , , . 2, 12 20 , — .



1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .



5 , 6 , match if let. Rust .



7 (API). 8 , , , -. 9 .



10 , , , . 11 , Rust . 12 grep, . , .



13 — , . 14 Cargo . 15 , , , .



16 , Rust . 17 Rust - , , , .



18 , Rust. 19 , , Rust, , , .

20 , !



, . Rust, Rust, , , , Rust.



: -, ! - , , , . , , .



Rust — , : . , , . , , ! , , , , . , .



可以使用模式的地方



在Rust中,图案出现在很多地方,并且您经常在没有意识到的情况下使用它们!本节讨论模式有效的情况。



匹配表达分支



如第6章所述,我们在匹配表达式的分支中使用模式。形式上,match表达式定义为match关键字,然后定义为match的值,如果该值与该分支的模式匹配,则匹配的一个或多个分支由模式和要执行的表达式组成,例如:



   match  {
           => ,
           => ,
           => ,
   }


匹配表达式的要求之一是它们必须是全面的,从某种意义上说,必须在匹配中考虑所有可能的值。为了考虑所有可能的选项,您需要在最后一个分支中包含一个包含所有内容的模式:例如,与任何值匹配的变量名将始终起作用,从而涵盖所有剩余的情况。



特殊模式_会匹配任何内容,但不会绑定到变量,因此通常用于匹配的最后一类。_模式很有用,例如,如果您想忽略任何未指定的值。我们将在``忽略模式中的值''部分中更详细地考虑模式。



如果有条件



在第6章中,我们讨论了let语句主要是作为一种较短的写等效于仅匹配一种情况的match表达式的方式。或者,如果let可以具有匹配项,则else包含如果if let中的模式不匹配要执行的代码。



清单18.1展示了如果let,else if和else if语句也可以混合匹配。与使用match表达式(只能表达一个值以与模式进行比较)相比,这给了我们更大的灵活性。另外,一系列的if let,else if和else if语句中的条件不需要相互引用。



清单18.1中的代码显示了针对几种条件的一系列测试,这些条件决定了背景色应该是什么。在此示例中,我们创建了具有硬编码值的变量,真实程序可以从用户输入中检索这些值。



清单18.1混合if let,else if,else if let和else语句



src/main.rs
      fn main() {
             let favorite_color: Option<&str> = None;
             let is_tuesday = false;
             let age: Result<u8, _> = "34".parse();

      (1) if let Some(color) = favorite_color {
          (2) println!("   , {},   ", color);
      (3) } else if is_tuesday {
          (4) println!(" -  !");
      (5) } else if let Ok(age) = age {
          (6) if age > 30 {
               (7) println!("     ");
              } else {
               (8) println!("     ");
              }
      (9) } else {
          (10) println!("     ");
           }
     }


如果用户指定喜欢的颜色(1),则这是背景颜色(2)。如果今天是星期二(3),则背景色为绿色(4)。如果用户将年龄指定为字符串,并且我们可以成功地将其解析为数字(5),则颜色取决于数字(6)的颜色是紫色(7)还是橙色(8)。如果这些条件都不适用(9),则背景色为蓝色(10)。



这种条件结构允许复杂的要求。使用此处的硬编码值,此示例将输出



     .


您可以看到一个if let表达式还可以以与match表达式的套管相同的方式引入阴影变量:if let Ok(age)= age(5)代码行引入了一个新的阴影变量age,其中包含Ok变量内部的值。这意味着如果年龄> 30,则需要在该块(6)中放入条件:如果让Ok(年龄)= age && age> 30,则不能在语句中合并这两个条件。我们要与30比较的阴影变量年龄,直到新作用域以大括号开头之前都是无效的。



使用if let语句的缺点是,编译器不检查穷举,而对match语句检查。如果我们跳过了最后一个else(9)块,因此跳过了某些情况的处理,则编译器将不会就可能的逻辑错误警告我们。



虽然让条件循环



在构造上与if let语句相似,while let条件循环允许while循环在模式匹配时运行。清单18.2中的示例显示了while let循环,该循环使用一个向量作为堆栈,并以与它们添加顺序相反的顺序输出向量中的值。



清单18.2 使用while let循环来输出值,而stack.pop()返回一些



    let mut stack = Vec::new();

    stack.push(1);
    stack.push(2);
    stack.push(3);

    while let Some(top) = stack.pop() {
          println!("{}", top);
    }


本示例打印3、2,然后打印1。pop方法从Vector中获取最后一个元素,并返回Some(值)。如果向量为空,则pop返回None。while循环继续执行其块中的代码,直到pop返回Some。当pop返回None时,循环停止。我们可以使用一会儿让条件循环从堆栈中删除每个项目。



对于循环



在第3章中,我们提到了for循环是Rust代码中最常见的循环结构,但是我们还没有讨论for的模式。在for循环中,模式是紧随for关键字之后的值,因此在y中的for x中,模式是x。



清单18.3展示了在for循环中使用模式来对for中的元组进行解构或分解。



清单18.3 在for循环中使用模式来破坏元组



   let v = vec!['a', 'b', 'c'];

   for (index, value) in v.iter().enumerate() {
        println!("{}    {}", value, index);
   }


清单18.3中的代码显示以下内容:



       0
   b    1
       2


我们使用enumerate方法重写迭代器,以生成迭代器中放置在元组中的值和该值的索引。第一次调用枚举方法将生成一个元组(0,'a')。当此值与(索引,值)模式组合时,索引为0且值为'a',则输出第一行数据。



让语句



在本章之前,我们直接讨论了仅将模式与match和if let语句一起使用,但是实际上我们在其他地方(包括在let语句中)使用了模式。考虑一种使用let传递变量值的简单方法:



let x = 5;


在整本书中,我们已经多次使用这种let语句,虽然您可能没有意识到,但您一直在使用模式!更正式地说,let语句如下所示:



let  = ;


在诸如let x = 5;之类的语句中,在PATTERN插槽中有一个变量名,变量名只是模式的一种简单形式。 Rust将表达式与模式进行比较,并分配其找到的任何名称。因此,在示例中,令x = 5;模式是x,表示“将此处匹配的内容与变量x关联”。由于名称x代表整个模式,因此该模式有效地意味着“将所有值绑定到变量x,无论​​其值如何”。



为了更清楚地看到到let语句模式的映射,请考虑清单18.4,该清单使用let模式对元组进行解构。



清单18.4使用模式解构元组并一次创建三个变量



let (x, y, z) = (1, 2, 3);


在这里,我们将元组映射到模式。Rust将(1、2、3)与(x,y,z)进行比较,发现此值与模式匹配,因此Rust将1与x关联,将2与y关联,将3与z关联。您可以将这个元组模式看作是在其中嵌套三个单独的变量模式。

如果模式中的元素数量与元组中的元素数量不匹配,则聚合类型将不匹配,我们将收到编译器错误。例如,清单18.5显示了试图将三元组分解为两个变量的尝试,但这将不起作用。



清单18.5 模式的构造不正确,其变量与元组中的元素数量不一致



let (x, y) = (1, 2, 3);


尝试编译此代码会导致如下错误:



error[E0308]: mismatched types
  --> src/main.rs:2:9
   |
2 |        let (x, y) = (1, 2, 3);
   |           ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
   |
   = note: expected type `({integer}, {integer}, {integer})`
                    found type `(_, _)`


如果我们想忽略一个元组中的一个或多个值,则可以使用_或..,如您将在``忽略模式中的值''部分中所见。如果问题在于模式中的变量太多,则需要通过删除变量使类型匹配,以使变量数等于元组中的元素数。



功能参数



功能参数也可以是模式。现在,您已经熟悉了清单18.6中的代码,该代码声明一个函数foo,该函数接受一个i32类型的参数x。



清单18.6 函数签名在参数中使用模式



fn foo(x: i32) {
     //   
}


x部分是一个模式!与let一样,我们可以将函数参数中的元组与模式匹配。当我们在函数内部传递元组时,清单18.7分解了元组中的值。



清单18.7 具有可破坏元组的参数的函数



src/main.rs
      fn print_coordinates(&(x, y): &(i32, i32)) {
            println!(" : ({}, {})", x, y);
      }

      fn main() {
            let point = (3, 5);
            print_coordinates(&point);
      }


该代码输出



 : (3, 5)


值&(3,5)与模式&(x,y)匹配,因此x为3且y为5.

此外,我们可以像在函数参数列表中一样在闭包参数列表中使用模式闭包与函数相似,如第13章中所述。



您已经看到了几种使用模式的方法,但是它们在使用它们的地方并不一样。在某些情况下,这些模式必须是不可辩驳的;在其他情况下,它们可以是可辩驳的。我们将在下面讨论这两个概念。



可引用性:模式不匹配的可能性



模式有两种味道:可辩驳和不可辩驳。与传递的任何可能值匹配的模式是无可辩驳的。一个示例是let x = 5;语句中的x,因为x完全匹配所有内容,因此不能匹配。可以反驳某些可能含义不符的模式。一个示例是if let Some(x)= a_value语句中的Some(x),因为如果a_value中的值为None而不是Some,则Some(x)将不匹配。



函数参数,let语句和for循环只能接受不可辩驳的模式,因为当值不匹配时程序无法做任何有意义的事情。 if let和while let表达式仅接受可反模式,因为根据定义它们被设计为处理可能的错误:条件表达式的功能是其根据成功或失败执行不同操作的能力。



通常,您不必担心可重用和不可重用模式之间的区别。但是,您仍然需要了解可抵扣性的概念,以便在错误消息中看到它时做出反应。在这些情况下,您将需要更改模式或使用该模式的构造,具体取决于代码的预期行为。



让我们看一下当我们在Rust要求使用不可辩驳的模式的地方尝试使用不可辩驳的模式时会发生什么,反之亦然。清单18.8显示了一个let语句,但是对于模式我们指定了Some(x),这是一个可反驳的模式。如您所料,此代码不会编译。



清单18.8 尝试使用带有let的可反模式



let Some(x) = some_option_value;


如果some_option_value为None,则它将与Some(x)模式不匹配,即该模式是可重复使用的。但是,let语句只能接受不可辩驳的模式,因为代码无法对值None进行任何有效的操作。在编译时,Rust会抱怨我们试图在需要不可重用模式的情况下尝试使用可重用模式:



error[E0005]: refutable pattern in local binding: `None` not covered
  -->
   |
3 | let Some(x) = some_option_value;
   |     ^^^^^^^ pattern `None` not covered


由于我们没有覆盖(也没有覆盖!)带有Some(x)模式的每个有效值,因此Rust正确地引发了编译器错误。



要解决此问题,当我们有一个可重复使用的模式而不是一个不可更改的模式时,我们可以更改使用该模式的代码:代替let,我们可以使用if let。然后,如果模式不匹配,则花括号中的代码将被跳过,并且工作将继续正确进行。清单18.9显示了如何修复清单18.8中的代码。



清单18.9使用if let语句和可反击的模式块代替let



if let Some(x) = some_option_value {
    println!("{}", x);
}


代码准备好了!这绝对是正确的代码,尽管这意味着我们不能毫无错误地使用不可辩驳的模式。如果我们给if let表达式提供一个始终匹配的模式(如清单18-10所示的x),那么它将不会编译。



清单18.10 尝试通过if let语句使用不可辩驳的模式



if let x = 5 {
    println!("{}", x);
};


编译器抱怨说,使用带有不可辩驳模式的if let表达式是没有意义的:



error[E0162]: irrefutable if-let pattern
  --> <anon>:2:8
   |
2 | if let x = 5 {
   |        ^ irrefutable pattern


因此,match表达式的存储区必须使用可重复使用的模式,最后一个存储区除外,最后一个存储区必须匹配不可恢复的模式中的所有剩余值。Rust允许在一个表达式中仅使用一个套就可以使用不可辩驳的模式,但是这种语法并不是特别有用,可以用更简单的let语句代替。



现在您知道了模式的使用位置以及可重用和不可重用的模式有何不同,让我们熟悉可用于创建模式的语法。



关于作者



Steve Klabnik领导Rust文档团队,是该语言的主要开发人员之一。他是一名经常讲师,并编写了大量开放源代码。以前曾从事Ruby和Ruby on Rails等项目。



Carol Nichols是Rust Core开发团队的成员,并且是Integer 32,LLC(世界上第一家专注于Rust的软件开发咨询公司)的联合创始人。Nichols是“ Rust语言” Rust Belt会议的组织者。



»有关这本书的更多详细信息,请参见出版商的网站

»目录

»摘录



给居住者的优惠券可享受25%的折扣-Rust



在为该书的纸质版本付款后,会向该电子邮件发送一本电子书。



All Articles