Rust权威指南

作者史蒂夫·克拉伯尼克 卡罗尔·尼科尔斯分类计算机-计算机综合
推荐值89.2 %来源微信读书
笔记数量35评论数量

第1章 入门指南

Hello, World!

  • Rust中所有以!结尾的调用都意味着你正在使用一个宏而不是普通函数

Hello, Cargo!

  • Cargo是Rust工具链中内置的构建系统及包管理器。由于它可以处理众多诸如构建代码、下载编译依赖库等琐碎但重要的任务,所以绝大部分的Rust用户都会选择它来管理自己的Rust项目。
  • 我们可以通过cargo build或cargo check来构建一个项目。• 我们可以通过cargo run来构建并运行一个项目。• 构建产生的结果会被Cargo存储在target/debug目录下,而非代码所处的位置。
  • 你可以使用命令cargo build --release在优化模式下构建并生成可执行程序。它生成的可执行文件会被放置在target/release目录下,而不是之前的target/debug目录下。这种模式会以更长的编译时间为代价来优化代码,从而使代码拥有更好的运行时性能

第4章 认识所有权

什么是所有权

  • 变量s离开作用域的地方。Rust在变量离开作用域时,会调用一个叫作drop的特殊函数
  • 这与我们刚刚学到的内容似乎有些矛盾:即便代码没有调用clone,x在被赋值给y后也依然有效,且没有发生移动现象。这是因为类似于整型的类型可以在编译时确定自己的大小,并且能够将自己的数据完整地存储在栈中,对于这些值的复制操作永远都是非常快速的。
  • 一旦某种类型拥有了Copy这种trait,那么它的变量就可以在赋值给其他变量之后保持可用性。

引用与借用

  • 这些&代表的就是引用语义,它们允许你在不获取所有权的前提下使用值。
  • 但可变引用在使用上有一个很大的限制:对于特定作用域中的特定数据来说,一次只能声明一个可变引用
  • 数据竞争(data race)与竞态条件十分类似,它会在指令满足以下3种情形时发生:• 两个或两个以上的指针同时访问同一空间。• 其中至少有一个指针会向空间中写入数据。• 没有同步数据访问的机制。数据竞争会导致未定义的行为,由于这些未定义的行为往往难以在运行时进行跟踪,也就使得出现的bug更加难以被诊断和修复。Rust则完美地避免了这种情形的出现,因为存在数据竞争的代码连编译检查都无法通过!
  • 我们不能在拥有不可变引用的同时创建可变引用。不可变引用的用户可不会希望他们眼皮底下的值突然发生变化!不过,同时存在多个不可变引用是合理合法的,对数据的只读操作不会影响到其他读取数据的用户。

切片

  • Rust的范围语法..有一个小小的语法糖:当你希望范围从第一个元素(也就是索引值为0的元素)开始时,则可以省略两个点号之前的值。
  • 字符串字面量就是切片

第5章 使用结构体来组织相关联的数据

定义并实例化结构体

  • 需要注意的是,一旦实例可变,那么实例中的所有字段都将是可变的。
  • 通过结构体更新语法,我们可以使用更少的代码来实现完全相同的效果,如示例5-7所示。这里的双点号..表明剩下的那些还未被显式赋值的字段都与给定实例拥有相同的值。
  • ,你还可以使用另外一种类似于元组的方式定义结构体,这种结构体也被称作元组结构体。元组结构体同样拥有用于表明自身含义的名称,但你无须在声明它时对其字段进行命名,仅保留字段的类型即可。一般来说,当你想要给元组赋予名字,并使其区别于其他拥有同样定义的元组时,你就可以使用元组结构体。在这种情况下,像常规结构体那样为每个字段命名反而显得有些烦琐和形式化了。

一个使用结构体的示例程序

  • Rust确实包含了打印调试信息的功能,但我们必须为自己的结构体显式地选择这一功能。为了完成该声明,我们在结构体定义前添加了#[derive(Debug)]注解,

方法

  • 方法与函数十分相似:它们都使用fn关键字及一个名称来进行声明;它们都可以拥有参数和返回值;另外,它们都包含了一段在调用时执行的代码。但是,方法与函数依然是两个不同的概念,因为方法总是被定义在某个结构体(或者枚举类型、trait对象,我们会在第6章和第17章分别介绍它们)的上下文中,并且它们的第一个参数永远都是self,用于指代调用该方法的结构体实例。
  • 为了在Rectangle的上下文环境中定义这个函数,我们需要将area函数移动到一个由impl(implementation)关键字❶起始的代码块中❷,并把签名中的第一个参数(也是唯一的那个参数)和函数中使用该参数的地方改写为self。
  • 使用方法替代函数不仅能够避免在每个方法的签名中重复编写self的类型,还有助于我们组织代码的结构。我们可以将某个类型的实例需要的功能放置在同一个impl块中,从而避免用户在代码库中盲目地自行搜索它们。
  • impl块还允许我们定义不用接收self作为参数的函数。由于这类函数与结构体相互关联,所以它们也被称为关联函数(associated function)。我们将其命名为函数而不是方法,是因为它们不会作用于某个具体的结构体实例。你曾经接触过的String::from就是关联函数的一种。关联函数常常被用作构造器来返回一个结构体的新实例。
  • 类似go中的constructor,cpp中的static
    impl块还允许我们定义不用接收self作为参数的函数。由于这类函数与结构体相互关联,所以它们也被称为关联函数(associated function)。我们将其命名为函数而不是方法,是因为它们不会作用于某个具体的结构体实例。你曾经接触过的String::from就是关联函数的一种。 关联函数常常被用作构造器来返回一个结构体的新实例。

第6章 枚举与模式匹配

定义枚举

  • 枚举允许我们直接将其关联的数据嵌入枚举变体内。我们可以使用枚举来更简捷地表达出上述概念,而不用将枚举集成至结构体中。在新的IpAddr枚举定义中,V4和V6两个变体都被关联上了一个String值:enum IpAddr { V4(String), V6(String),}
  • 类似cpp中的union
    枚举允许我们直接将其关联的数据嵌入枚举变体内。我们可以使用枚举来更简捷地表达出上述概念,而不用将枚举集成至结构体中。在新的IpAddr枚举定义中,V4和V6两个变体都被关联上了一个String值: enum IpAddr { V4(String), V6(String), }
  • 枚举和结构体还有一点相似的地方在于:正如我们可以使用impl关键字定义结构体的方法一样,我们同样可以定义枚举的方法。
  • Rust并没有像许多其他语言一样支持空值。空值(Null)本身是一个值,但它的含义却是没有值。在设计有空值的语言中,一个变量往往处于这两种状态:空值或非空值。
  • 假如我们使用了None而不是Some变体来进行赋值,那么我们需要明确地告知Rust这个Option<T>的具体类型。这是因为单独的None变体值与持有数据的Some变体不一样,编译器无法根据这些信息来正确推导出值的完整类型。
  • 为什么Option<T>的设计就比空值好呢?简单来讲,因为Option<T>和T(这里的T可以是任意类型)是不同的类型,所以编译器不会允许我们像使用普通值一样去直接使用Option<T>的值。
  • Option将空值检查放在了编译阶段
    为什么Option<T>的设计就比空值好呢? 简单来讲,因为Option<T>和T(这里的T可以是任意类型)是不同的类型,所以编译器不会允许我们像使用普通值一样去直接使用Option<T>的值。

控制流运算符match

  • 匹配分支另外一个有趣的地方在于它们可以绑定被匹配对象的部分值,而这也正是我们用于从枚举变体中提取值的方法

简单控制流if let

  • 使用if let意味着你可以编写更少的代码,使用更少的缩进,使用更少的模板代码。但是,你也放弃了match所附带的穷尽性检查。究竟应该使用match还是if let取决于你当时所处的环境,这是一个在代码简捷性与穷尽性检查之间取舍的过程。

第7章 使用包、单元包及模块来管理日渐复杂的项目

包与单元包

  • 首先,一个包中只能拥有最多一个库单元包。其次,包可以拥有任意多个二进制单元包。最后,包内必须存在至少一个单元包(库单元包或二进制单元包)。
  • 最初生成的包只包含源文件src/main.rs,这也意味着它只包含一个名为my-project的二进制单元包。而假设包中同时存在src/main.rs及src/lib.rs,那么其中就会分别存在一个二进制单元包与一个库单元包,它们拥有与包相同的名称。我们可以在路径src/bin下添加源文件来创建出更多的二进制单元包,这个路径下的每个源文件都会被视作单独的二进制单元包。

通过定义模块来控制作用域及私有性

  • src/main.rs与src/lib.rs被称作单元包的根节点,因为这两个文件的内容各自组成了一个名为crate的模块,并位于单元包模块结构的根部。这个模块结构也被称为模块树(module tree)。

用于在模块树中指明条目的路径

  • 而当我们单独将eat_at_restaurant移动至dining模块时,指向add_to_waitlist的绝对路径会保持不变,但对应的相对路径则需要手动更新。大部分的Rust开发者会更倾向于使用绝对路径,因为我们往往会彼此独立地移动代码的定义与调用代码。
  • 处于父级模块中的条目无法使用子模块中的私有条目,但子模块中的条目可以使用它所有祖先模块中的条目。
  • 我们从crate,也就是单元包的模块树的根节点开始
  • 枚举与结构体之所以不同,是由于枚举只有在所有变体都公共可用时才能实现最大的功效,而必须为所有枚举变体添加pub则显得烦琐了一些,因此所有的枚举变体默认都是公共的。对于结构体而言,即便部分字段是私有的也不会影响到它自身的使用,所以结构体字段遵循了默认的私有性规则,除非被标记为pub,否则默认是私有的

阅读明细

维度指标
累积阅读天数11天
最长连续阅读天数4天
单日阅读最久37分 (2024/01/18)
阅读笔记条数38条
日期阅读时长
2023/06/0710分
2023/12/1312分
2023/12/1516分
2024/01/0813分
2024/01/143分
2024/01/1725分
2024/01/1837分
2024/01/2022分
2024/01/215分
2024/01/222分
2024/01/237分
See all book notesSee all book notes