31 KiB
附录
以下小节包含了在咱们的 Rust 路途中,会发现有用的一些参考资料。
附录 A:关键字
以下清单包含了 Rust 语言当前或今后要用到的一些关键字。由此,他们便不能被用作标识符(除在 “原始标识符” 小节中咱们将讨论的那些外)了。所谓标识符,是函数、变量、参数、结构体字段、模组、代码箱、常量、宏、静态值、属性、类型、特质或生命周期等的名字。
当前在用的关键字
下面是当前在用关键字的清单,带有其作用描述。
as- 执行原生强制转换,primitive casting,消除包含着某个项目的特定特质歧义,disambiguate the specific trait containing a item,或重命名use语句中的项目;async- 返回一个Future类型值,而非阻塞当前线程;await- 在某个Future值的结果准备好前,暂停程序执行;break- 立即退出某个循环;const- 定义出常量项目或常量原始指针;continue- 继续下一循环迭代;crate- 在模组路径中,指向代码箱根;dyn- 动态调遣到某个特质对象,参考 特质对象执行动态调遣;else-if的回退,及if let控制流的构件;extern- 链接外部函数或变量;false- 布尔值假的字面值;fn- 定义出某个函数或函数指针类型;for- 对某个迭代器的项目加以迭代、实现某个特质,或指明某个更高级别的生命周期,a higher-ranked lifetime;if- 基于某个条件表达式结果的分支;impl- 实现固有或特质功能,implement inherent or trait functionality;in-for循环语法的一部分;let- 绑定某个变量;loop- 无条件地循环;match- 将某个值与模式匹配;mod- 定义出模组;move- 领导闭包取得其所有捕获值的所有权;mut- 注解出引用、原始指针或模式绑定等中的可变性;pub- 注解出结构体、impl代码块或模组等中的公开可见性;ref- 按引用绑定;return- 自函数返回值;Self- 咱们正定义或实现中类型的类型别名;self- 方法主体,method subject,或当前模组;static- 在整个程序执行过程持续有效的全局变量或生命周期;struct- 定义出某个结构体;super- 当前模组的父模组;trait- 定义出某个特质;true- 布尔值真的字面值;type- 定义出某个类型别名或关联类型;union- 定义出某个 联合体,是在联合体声明时用到的唯一关键字;unsafe- 注解非安全代码、函数、特质或一些实现;use- 将符号带入到作用域;where- 注解约束某个类型的子句;while- 基于某个表达式结果而有条件的循环。
为今后使用保留的关键字
以下关键字尚无任何功能,但被 Rust 为今后的潜在使用而保留。
abstractbecomeboxdofinalmacrooverrideprivtrytypeofunsizedvirtualyield
原始标识符
原始标识符,raw identifiers 属于允许实现使用一般不被允许关键字的语法。是通过在关键字前加上前缀 r#,使用原始标识符的。
比如,match 是个关键字。在咱们尝试编译下面这个使用 match 作其名字的函数时:
文件名:src/main.rs
fn match(needle: &str, haystack: &str) -> bool {
haystack.contains(needle)
}
咱们将得到这样的报错:
error: expected identifier, found keyword `match`
--> src/main.rs:1:4
|
1 | fn match(needle: &str, haystack: &str) -> bool {
| ^^^^^ expected identifier, found keyword
该报错显示咱们无法将关键字 match 用作函数标识符。要将 match 用作函数名字,咱们就需要使用原始标识符语法,像下面这样:
文件名:src/main.rs
fn r#match(needle: &str, haystack: &str) -> bool {
haystack.contains(needle)
}
fn main() {
assert! (r#match("foo", "foobar"));
}
此代码将不带任何错误地编译。请注意那个函数的定义中,与 main 中该函数被调用处其名字上的 r# 前缀。
原始标识符实现了将任何咱们所选的词语用作标识符,即使那个词语碰巧是个保留的关键字。这给到咱们更自由地选择标识符名字,以及实现与一些以其中这些词语不属于关键字的语言,所编写的程序集成。此外,原始标识符实现了,对那些以不同于咱们代码箱 Rust 版本编写库加以运用。比如,在 2015 版中 try 就不是个关键字,但在 2018 版本中却是。若咱们依赖于一个使用 2015 版本编写的库,而该库有一个 try 函数,那么咱们就将需要在这种情况下,使用原始标识符 r#try,来从咱们的 2018 版本的代码,调用那个函数。请参阅 附录 E 了解更多有关版本的信息。
附录 B:运算符与符号
此附录包含了 Rust 语法的词汇表,包括运算符及别的一些,自己单独出现或出现于路径、泛型、特质边界、宏、属性、注释、元组及方括符等上下文中的符号。
运算符
表 B-1 包含了 Rust 中的符号、该符号将如何出现于上下文中的一个示例、简单的解释,以及该运算符是否可过载。若某个运算符可以过载,就会列出过载那个运算符要用到的相关特质。
表 B-1:运算符
| 运算符 | 示例 | 说明 | 是否可以过载 |
|---|---|---|---|
! |
ident! (...) ident! {...} ident! [...] |
宏扩展 | |
! |
!expr |
按位或逻辑求补运算 | 否 |
!= |
expr != expr |
不等比较 | PartialEq |
% |
expr % expr |
算术求余运算 | Rem |
%= |
var %= expr |
算术求余并赋值 | RemAssign |
& |
&expr, &mut expr |
借用 | |
& |
&type, &mut type, &'a type, &'a mut type |
借用指针类型 | |
& |
expr & expr |
按位与(AND)运算 | BitAnd |
&= |
var &= expr |
按位与(AND)运算并赋值 | BitAndAssign |
&& |
expr && expr |
短路逻辑与(AND)运算,short-circuit logical AND | |
* |
expr * expr |
算术乘法运算 | Mul |
*= |
var *= expr |
算术乘法运算并赋值 | MulAssign |
* |
*expr |
解引用运算 | Deref |
* |
*const type, *mut type |
原始指针运算 | |
+ |
trait + trait, 'a + trait |
复合类型约束运算 | |
+ |
expr + expr |
算术加法运算 | Add |
+= |
var += expr |
算术加法运算并赋值 | AddAssign |
, |
expr, expr |
参数与元素分隔符 | |
- |
- expr |
算术取反运算 | Neg |
- |
expr - expr |
算术减法运算 | Sub |
-= |
var -= expr |
算术减法运算并赋值 | SubAssign |
-> |
fn(...) -> type, |...| -> type |
函数与闭包的返回值类型 | |
. |
expr.ident |
成员访问 | |
.. |
.., expr.., ..expr, expr..expr |
排除右侧的范围语法字面值 | PartialOrd |
..= |
..=expr, expr..=expr |
包含右侧范围语法字面值 | PartialOrd |
.. |
..expr |
结构体更新语法 | |
.. |
variant(x, ..), struct_type { x, .. } |
“等等” 模式绑定,"And the rest" pattern binding | |
... |
expr...expr |
(已弃用,请使用 ..= 代替)在模式中:包含式范围模式 |
|
/ |
expr / expr |
算术除法运算 | Div |
/= |
var /= expr |
算术除法并赋值 | DivAssign |
: |
pat: type, ident: type |
约束 | |
: |
ident: expr |
结构体字段初始化 | |
: |
'a: loop {...} |
循环标签 | |
; |
expr; |
语句及项目的终止符 | |
; |
[..., len] |
固定大小数组语法的一部分 | |
<< |
expr << expr |
向左移位运算 | Shl |
<<= |
var <<= expr |
向左移位运算并赋值 | ShlAssign |
< |
expr < expr |
小于比较 | PartialOrd |
<= |
expr <= expr |
小于等于比较 | PartialOrd |
= |
var = expr, ident = type |
赋值/等价,equivalence | |
== |
expr == expr |
相等比较 | PartialEq |
=> |
pat => expr |
匹配支臂语法的一部分 | |
> |
expr > expr |
大于比较 | PartialOrd |
>= |
expr >= expr |
大于等于比较 | PartialOrd |
>> |
expr >> expr |
向右位移运算 | Shr |
>>= |
var >>= expr |
向右位移运算并赋值 | ShrAssign |
@ |
ident @ pat |
模式绑定 | |
^ |
var ^ expr |
按位异或运算 | BitXor |
^= |
var ^= expr |
按位异或运算并赋值 | BitXorAssign |
| |
pat | pat |
模式选择,pattern alternatives | |
| |
expr | expr |
按位或(OR)运算 | BitOr |
|= |
var |= expr |
按位或(OR)运算并赋值 | BitOrAssign |
|| |
expr || expr |
短路逻辑或运算,Short-circuiting logical OR | |
? |
expr? |
错误传递 |
非运算符的符号
Non-operator Symbols
以下清单包含了不以运算符发挥作用的全部符号;那就是说,他们不会表现得像函数或方法调用。
表 B-2 给出了自己单独出现,并在多种场合有效的一些符号。
表 B-2:独立语法,Stand-Alone Syntax
| 符号 | 说明 |
|---|---|
'ident |
命名的生命周期或循环标签 |
...u8, ...i32, ...f64, ...usize 等等 |
指定类型的数字字面值 |
"..." |
字符串字面值 |
r"...", r#"..."#, r##"..."## 等等 |
原始字符串字面值,其中的转义字符不会被处理 |
b"..." |
字节字符串字面值;构造出一个字节数组而非字符串 |
br"...", br#"...", br##"..."## 等等 |
原始字节字符串字面值,是原始与字节字符串字面值的结合 |
'...' |
字符字面值 |
b'...' |
ASCII 字节字面值 |
|...| expr |
闭包 |
! |
发散函数下总是空的底部类型,always empty bottom type for diverging functions |
_ |
“忽略,ignored” 模式绑定;还用于令到整数字面值可读,also used to make integer literals readable |
表 B-3 展示了出现在模组层次结构中到某个项目路径上下文中的一些符号。
表 B-3:路径相关的语法
| 符号 | 说明 |
|---|---|
ident::ident |
命名空间路径 |
::path |
相对于代码箱根的路径(比如,某个显式绝对路径) |
self::path |
相对于当前模组的路径(比如,某个显式相对路径) |
super::path |
相对于当前模组父模组的路径 |
type::ident, <type as trait>::ident |
关联的常量、函数及类型 |
<type>::... |
无法直接命名的某个类型的关联项目(比如,<&T>::..., <[T]>::... 等等) |
trait::method(...) |
通过命名出定义方法的类型,消除该方法调用的歧义 |
<type as trait>::method(...) |
通过命名出特质与类型,消除方法调用的歧义 |
表 B-4 展示了出现在运用泛型参数上下文中的一些符号。
表 B-4:泛型
| 符号 | 说明 |
| :--- | :--- |
| path<...> | 指明类型中的泛型参数(比如,Vec<u8>) |
| path::<...>, method::<...> | 指明表达式中泛型、函数或方法的参数;通常这被称作涡轮鱼语法,turbofish(比如,"42".parse::<i32>(),关于 Rust 的 turbofish 语法,请参考:What is Rust's turbofish),RUST 中的 turbofish 语法(一) ... |
| fn ident<...> ... | 定义出泛型函数 |
| struct ident<...> ... | 定义出泛型结构体 |
| enum ident<...> ... | 定义出泛型枚举 |
| impl<...> ... | 定义出泛型实现 |
| for<...> type | 高阶声明周期边界,higher-ranked lifetime bounds |
| type<ident=type> | 其中一个或更多的关联类型有着指定赋值的某种泛型(a generic type where one or more associated types have specific assignments,比如,Iterator<Item=T>)
下表 B-5 展示了出现在使用特质边界的约束性泛型参数上下文中的一些符号,table B-5 shows symbols that appear in the context of constraining generic type parameters with trait bounds。
B-5:特质边界约束,Trait Bound Constrains
| 符号 | 说明 |
|---|---|
T: U |
泛型参数 T 受实现了 U 的类型约束 |
T: 'a |
泛型 T 必须要比生命周期 'a 活得更久,generic type T must outlive lifetime 'a(意思是该类型不能间接地包含任何生命周期短于 'a 的引用) |
T: 'static |
泛型 T 不包含除 'static 的引用外的其他引用 |
'b: 'a |
泛型生命周期 'b 必须要比 'a 存活得更久 |
T: ?Sized |
允许泛型参数为动态大小类型 |
'a + trait, trait + trait |
复合的类型约束 |
下表 B-6 展示了出现在宏调用或定义上下文中,并指明了某个项目上属性的一些符号。
B-6:宏与属性
| 符号 | 说明 |
|---|---|
#[meta] |
外层属性 |
#![meta] |
内层熟悉 |
$ident |
宏代换,macro substitution |
$ident:kind |
宏捕获 |
$(...) ... |
宏重复,macro repetition |
ident! (...), ident! {...}, ident! [...] |
宏调用,macro invocation |
下表 B-7 展示了创建注释的一些符号。
表 B-7:注释
| 符号 | 说明 |
|---|---|
// |
注释行 |
//! |
内层行文档注释,inner line doc comment |
/// |
外层行文档注释,outter line doc comment |
/*...*/ |
注释块 |
/*!...*/ |
内层块文档注释,inner block doc comment |
/**...*/ |
外层块文档注释,outter block doc comment |
下表 B-8 展示了出现于用到元组上下文中的一些符号。
元组
| 符号 | 说明 |
|---|---|
() |
空元组(又叫单元值),同时属于字面值与类型 |
(expr) |
元括号括起来的表达式,parenthesized expression |
(expr,) |
单一元素的元组表达式 |
(type,) |
单一元素的元组类型,single-element tuple type |
(expr, ...) |
元组表达式 |
(type, ...) |
元组类型,tuple type |
expr(expr, ...) |
函数调用表达式;还用于初始化一些元组的 struct 以及元组的 enum 变种,function call expression; also used to initialize tuple structs and tuple enum vairants |
expr.0, expr.1 等等 |
对元组进行索引 |
下表 B-9 展示了其中用到花括号上下文中的一些符号。
表 B-9:花括号
| 符号 | 说明 |
|---|---|
{...} |
代码块表达式 |
Type {...} |
struct 的字面值 |
下表 B-10 展示了其中用到方括号上下文中的一些符号。
表 B-10:方括号
| 符号 | 说明 |
|---|---|
[...] |
数组的字面值 |
[expr; len] |
包含着 expr 的 len 拷贝数组的字面值 |
[type; len] |
包含着 len 个 type 的实例数组的字面值 |
expr[expr] |
对集合进行索引,collection indexing。是可过载的 (Index, IndexMut),overloadable (Index, IndexMut) |
expr[..], expr[a..], expr[..b], expr[a..b] |
用到了 Range、RangeFrom、RangeTo 或 RangeFull 作为 “索引”的,带有集合切片集合索引,collection indexing pretending to be collection slicing, using Range, RangeFrom, RangeTo, or RangeFull as the "index" |
附录 C:派生特质
Appendix C: Derivable Traits
本书的多个不同地方,咱们都曾讨论过 derive 属性,咱们可将其应用到结构体或枚举定义。derive 属性会在咱们以 derive 语法注解的类型上,生成将以某个特质自身默认实现,而实现该特质的代码。
在这个附录中,咱们会提供到标准库中,咱们可以与 derive 一起使用的全部特质的参考。以下各个小节均会讲到:
- 此特质将启用那些操作符与方法;
- 由
derive所提供到的该特质实现会做些什么; - 实现该特质对那个类型意味着什么;
- 允许及不允许实现该特质的情况;
- 需要该特质操作的示例。
若咱们想要不同于由 derive 属性所提供的行为,请参考 标准库文档,了解如何亲自实现各个特质的详细信息。
这里列出的这些特质,只是一些由标准库所提供的,可使用 derive 实现于咱们类型上的那些。定义在标准库中别的一些特质,则没有什么合理的默认行为,因此是否要以对于咱们正尝试完成的东西有意义的方式,实现他们就取决于咱们自己了。
不能派生的一个特质示例便是 Display,其为终端用户处理格式化。咱们应始终要考虑将某个类型显示给用户的恰当方式。终端用户应被允许看到该类型的哪些部分?他们会发现哪些部分是相关的?数据的何种形式才是与他们最为密切相关的?Rust 编译器并无这种见解,因此他就无法为咱们提供到恰当的默认行为。
这个附录中所提供到的派生特质清单并不详尽:库可以为他们自己的特质实现 derive,从而领导咱们可使用 derive 的特质清单为真正开放的。实现 derive 设计到使用程序性宏,这在第 19 章的 “关于宏” 小节讲到过。
输出给编程者的 Debug
Debug for Programmer Output
Debug 特质实现了格式字符串中的格式化,所谓格式字符串,即咱们通过在 {} 里添加 :? 所表示的。
Debug 特质允许咱们为调试目的打印某种类型的实例,如此咱们以及用到咱们类型的其他编程者,就可以在程序执行的某个特定时刻,就其某个实例加以探查。
在比如用到 assert_eq! 宏中等情况下,Debug 特质便是要求使用的。assert_eq! 这个宏在相等断言失败时,就会打印出作为参数所给到的两个实例值,如此编程者就可以看到为何这两个实例不相等。
用于相等比较的 PartialEq 与 Eq
PartialEq 特质允许咱们比较某种类型的两个实例,来检查他们是否相等,并实现 == 与 != 运算符的应用。
对 PartialEq 进行派生,就会实现 eq 方法。当 ParitalEq 实在结构体上实现的时,只有在两个实例的 全部 字段都相等时,他们才是相等的,且在有任何字段不等时,两个实例便不相等。当在枚举上派生时,枚举的各个变种与自身相等,而不等于其他任何变种。
在使用需要能够比较某个类型的两个实例是否相等的 assert_eq! 宏时,就需要这个 PartialEq 特质。
而 Eq 特质则没有方法。他的目的是要表明,所注解的类型的每个值,其值都等于他自身。尽管并非所有实现 PartialEq 的类型都可以实现 Eq,但 Eq 特质却只可应用到那些同时实现了 PartialEq 的类型。这方面的一个示例,便是浮点数类型:浮点数的实现,就表明两个非数字(the not-a-number, NaN)的值,是各自不相等的。
要求 Eq 的一个示例,就是 HashMap<K, V> 中的那些键,如此 HashMap<K, V> 就可以区分出两个键是否一致。
用于排序比较的 PartialOrd 与 Ord
PartialOrd and Ord for Ordering Comparisons
PartialOrd 特质实现为排序目的,而比较某种类型的那些实例。实现了 PartialOrd 的类型,便可与 <、>、<= 及 >= 符号一起使用了。咱们只能对那些同时实现了 PartialEq 的类型,应用这个 PartialOrd 特质。
派生 PartialOrd,会实现 partial_cmp 方法,该方法会返回一个在所给的那些值不会产生出顺序时,将为 None 的一个 Option<Ordering>。至于即使那种类型的大多数值都可被比较,但仍不会产生出顺序的值的一个示例,便是非数字(NaN)浮点值。在任何浮点数和非数字浮点值下调用 partial_cmp,都会返回 None。
在于结构体上派生时,PartialOrd 会通过字段出现在结构体定义中的顺序,比较每个字段中的值,比较两个实例。而当于枚举上派生时,枚举定义中较早声明的枚举变种,被当作是小于后面所列出的那些变种的。
在比如会产生出由范围表达式所指定范围中一个随机数的, rand 代码箱的 gen_range 方法来说,PartialOrd 特质便是需要的。
Ord 特质实现对所注解类型的任何两个值,将存在有效顺序的掌握。Ord 特质会实现 cmp 方法,由于有效排序将始终可行,因此该方法返回的是 Ordering 而非 Option<Ordering>。咱们只可对那些同时实现了 PartialOrd 及 Eq (而 Eq 要求 PartialEq) 的类型,实现这个 Ord 特质。当于结构体及枚举上派生 Ord 时,cmp 就会以与 PartialOrd 下 partial_cmp 的派生实现同样方式行事。
要求 Ord 的一个示例,即为将一些值存储在 BTreeSet<T> 这种根据值的排序,而存储数据的数据结构中时。
用于复制值的 Clone 与 Copy
Clone and Copy for Duplicating Values
Clone 特质实现了显式创建值的深拷贝,而该复制过程则可能涉及运行一些任意代码,arbitary code,与拷贝内存堆数据。请参阅第 4 章中 “变量与数据交互方式:克隆” 小节,了解更多有关 Clone 的信息。
派生 Clone 会实现 clone 方法,当对整个类型实现了这个方法时,其就会在该类型的各个部分上调用 clone。这意味着类型要派生 Clone 其中的全部字段或值,都必须同时实现 Clone。
需要 Clone 特质的一个示例,便是在切片上调用 to_vec 方法时。切片不持有其包含的那些类型实例,但自 to_vec 所返回的那个矢量值,却将需要持有他的那些实例,从而 to_vec 会调用各个条目上的 clone。因此,存储在切片中的类型,就必须实现 Clone。
Copy 特质实现了只通过拷贝存储在栈上的二进制位,而复制某个值;任意代码并无必要。请参阅第 4 章中 “唯栈数据:拷贝”,了解更多有关 Copy 的信息。
Copy 特质没有定义阻止编程者过载那些方法,及破坏不会有任意代码运行这个假设的任何方法。那样的话,所有编程者就都可以假定,拷贝值将会非常快。
咱们可在其组成部分都实现了 Copy 的任何类型上派生 Copy 特质。由于实现 Copy 的类型,都有着执行与 Copy 同样任务的一个 Clone 的简单实现,因此实现 Copy 的类型必须同时实现 Clone。
很少需要 Copy 特质;实现了 Copy 的类型,有着可供选择的优化方案,意味着咱们不必调用 clone,而调用 clone 会令到代码更简洁。
对于 Copy 下每种可能情况,咱们都可同时以 Clone 完成,除了代码可能更慢,或在一些地方不得不使用 clone。
用于将值映射到固定大小值的 Hash
Hash for Mapping a Value to a Value of Fixed Size
Hash 特质实现了取某种任意大小类型的实例,并通过使用散列函数,将那个实例映射到固定大小的值。派生 Hash 会实现 hash 方法。hash 放的派生实现,会将在该类型各个组成部分上调用 hash 的结果结合起来,这就意味着类型要派生 Hash,那么其全部字段,都必须同时实现 Hash。
要求 Hash 的一个示例,便是为了高效地存储数据,而在 Hash<K, V> 中存储那些键时。
用于默认值的 Default
Default for Default Values
Default 特质实现了为类型创建出一个默认值。派生 Default 会实现 default 函数。default 函数的派生实现,会在类型的各个部分上调用 default 函数,意味类型要派生 Defualt,其中的全部字段或值,都必须同时实现 Default。
Default::default 函数,通常是与第 5 章中 “使用结构体更新语法从其他实例创建出实例” 小节里曾讨论过的结构体更新语法结合使用的。咱们可以定制结构体的几个字段,并在随后通过使用 ..Default::default(),为其余字段设置并使用默认值。
在 Option<T> 实例上使用 unwrap_or_default 方法时,便是需要 Default 特质的一个示例。当那个 Option<T> 为 None 时,方法 unwrap_or_default 就将返回存储在 Option<T> 中,那个类型 T 的 Default::default 结果。
附录 D:一些有用开发工具
在此附录中,咱们会讲到 Rust 项目所提供的一些有用的开发工具。咱们将看看自动格式化、应用警告修复的一些快速方法、一种代码静态分析工具,a linter,以及与多种 IDE 的集成。
使用 rustfmt 的自动格式化
Automatic Formatting with rustfmt
rustfmt 工具会依据社区编码风格,重新格式化咱们的代码。许多协作项目,都使用了 rustfmt 来防止有关编写 Rust 时使用何种风格方面的争论:每个人都使用这个工具来格式化他们的代码。
要安装 rustfmt,请键入下面的命令:
$ rustup component add rustfmt
如同 Rust 会同时给到 rustc 与 cargo 一样,此命令会给到咱们 rustfmt 与 cargo-fmt。要格式化任何 Cargo 项目,请敲入下面的命令:
$ cargo fmt
运行此命令,会重新格式化当前代码箱中全部的 Rust 代码。这只会改变编码风格,而不会改变代码语义。关于 rustfmt 的更多信息,请参阅 其文档.
使用 rustfix 修复咱们的代码
Fix Your Code with rustfix
rustfix 工具已被 Rust 安装所包含,并可大致以咱们想要的方式,修复那些有着明确纠正问题方法的一些编译器告警。咱们之前大概率已经见到过编译器告警了。比如,设想有下面这段代码:
文件名:src/main.rs
fn do_something() {}
fn main() {
for i in 0..100 {
do_something();
}
}
此处,咱们正调用 do_something 函数 100 次,但咱们在 for 循环的代码体中,从未用到那个变量 i。Rust 就会就此对咱们发出告警:
$ cargo build
Compiling rustfix_demo v0.1.0 (/home/lenny.peng/rust-lang-zh_CN/rustfix_demo)
warning: unused variable: `i`
--> src/main.rs:4:9
|
4 | for i in 0..100 {
| ^ help: if this is intentional, prefix it with an underscore: `_i`
|
= note: `#[warn(unused_variables)]` on by default
warning: `rustfix_demo` (bin "rustfix_demo") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.29s
这个告警建议咱们要使用 _i 做名字:其中的下划线表示咱们有意不使用这个变量。通过运行 cargo fix 命令,咱们就可以使用 rustfix,自动应用那项建议:
$ cargo fix --allow-no-vcs
Checking rustfix_demo v0.1.0 (/home/lenny.peng/rust-lang-zh_CN/rustfix_demo)
Fixed src/main.rs (1 fix)
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
当咱们再次看到 src/main.rs,就将发现 cargo fix 已修改了这段代码:
文件名:src/main.rs
fn do_something() {}
fn main() {
for _i in 0..100 {
do_something();
}
}
那个 for 循环变量,现在就被命名为了 _i,同时那条告警也不再出现了。
咱们还可使用 cargo fix 命令,将咱们的代码在不同 Rust 版本之间转换。有关这些 Rust 版本,在附录 E 中有讲到。
使用 Clippy 获得更多的代码静态分析
More Lints with Clippy
Clippy 工具是用于分析咱们代码,从而咱们可以捕获到一些常见错误,而改进咱们 Rust 代码的一套代码静态分析集合。
要安装 Clippy,请输入以下命令:
$ rustup component add Clippy
在任何 Cargo 项目上要运行 Clippy 的静态分析,请输入以下命令:
$ cargo clippy
比如说咱们编写了像下面这个程序这样,用到某个数学常量近似值,好比说 pi,的一个程序:
文件名:src/main.rs
fn main() {
let x = 3.1415;
let r = 8.0;
println!("圆的面积为 {}", x * r * r);
}
在这个项目上运行 cargo clippy 就会得到下面的报错:
$ cargo clippy
Checking clippy_demo v0.1.0 (/home/lenny.peng/rust-lang-zh_CN/clippy_demo)
error: approximate value of `f{32, 64}::consts::PI` found
--> src/main.rs:2:13
|
2 | let x = 3.1415;
| ^^^^^^
|
= help: consider using the constant directly
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
= note: `#[deny(clippy::approx_constant)]` on by default
error: could not compile `clippy_demo` due to previous error
此报错让咱们明白,Rust 已经定义了一个更精确的 PI 常量,且当咱们使用这个常量时,咱们的程序将更为正确。那么咱们随后就应修改咱们代码为使用这个 PI 常量。下面的代码就捕获导致 Clippy 的任何错误或告警:
文件名:src/main.rs
fn main() {
let x = std::f64::consts::PI;
let r = 8.0;
println!("圆的面积为 {}", x * r * r);
}
有关 Clippy 的更多信息,请参阅 其文档。
用到 rust-analyzer 的 IDE 集成
IDE Integration Using rust-analyzer
为帮助 IDE 集成,Rust 社区建议使用 rust-analyzer。此工具是一套以编译器为中心,操 语言服务器协议,Language Server Protocol 的实用工具;而所谓语言服务器协议,则是用于各种 IDEs 和编程语言,二者相互之间通信的一种规格。有多种不同客户端可使用 rust-analyzer,比如 Visual Studio Code 的 Rust 分析器插件。
请访问 rust-analyzer 项目 主页,了解其安全说明,随后在咱们的特定 IDE 中安装该语言的服务器支持。咱们的 IDE 就能获得诸如自动补全、跳至定义及行内报错等能力。
附录 E:关于版本
Appendix E - Editions
在第一章中,咱们曾看到 cargo new 会把一点有关某个版的元数据,添加到咱们的 Cargo.toml 文件。此附录就会讲到那意味着什么!