仅是一些笔记,完整的rust学习文档详见https://rustwiki.org/zh-CN/book/title-page.html
1. 变量和可变性
1.1 变量
在rust中,变量默认都是不可变的,可在变量名前加上mut使变量可变
1.2 常量(constant)
常量是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异。
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
- 常量绝不可以加mut,而且常量使用const关键字,而不是let关键字来声明,并且值的类型必须注明。
- 常量可以在任意作用域内声明,包括全局作用域
- 常量只能设置为常量表达式,而不能是函数调用的结果或是只能在运行时计算得到的值。
- Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词。
- 在声明的作用域内,常量在程序运行的整个过程中都有效。对于应用程序域中程序的多个部分可能都需要知道的值的时候,常量是一个很有用的选择。
- 将整个程序中用到的硬编码(hardcode)值命名为常量,对于将该值的含义传达给代码的未来维护者很有用。如果将来需要更改硬编码的值,则只需要在代码中改动一处就可以了。
1.3 遮蔽(shadow)
在Rust中,可以声明和前面变量具有相同的变量名,其中第一个变量被第二个变量遮蔽,这意味着当我们使用变量时我们看到的会是第二个变量的值。可以通过使用相同的变量名并重复使用let关键字来遮蔽变量。
fn main() {
let x = 5;
let x = x + 1; // x=6
{
let x = x * 2; // x=12
println!("The value of x in the inner scope is: {}", x);
}
println!("The value of x is: {}", x); // x=6
}
请注意,遮蔽就是又创建了一个新变量,和使用mut声明一个可变变量是有本质不同的。所有遮蔽时可以使用let关键字改变变量的类型。
2. 数据类型
Rust 的每个值都有确切的数据类型(data type),该类型告诉 Rust 数据是被指定成哪类数据,从而让 Rust 知道如何使用该数据。在本节中,我们将介绍两种数据类型:标量类型和复合类型。
2.1 标量类型
标量(scalar)类型表示单个值。Rust 有 4 个基本的标量类型:整型、浮点型、布尔型和字符。
2.1.1 整数类型
Rust中内置的整数类型如下,
isize 和 usize 类型取决于程序运行的计算机体系结构,在表中表示为“arch”:若使用 64 位架构系统则为 64 位,若使用 32 位架构系统则为 32 位。
长度 | 有符号类型 | 无符号类型 |
---|---|---|
8 位 | i8 | u8 |
16 位 | i16 | u16 |
32 位 | i32 | u32 |
64 位 | i64 | u64 |
128 位 | i128 | u128 |
arch | isize | usize |
注:在Rust中如果以release的形式进行构建,不会检测整型溢出的问题,此变量的值将不是期望的值。
2.1.2 浮点类型
Rust 的浮点型是 f32 和 f64,它们的大小分别为 32 位和 64 位。默认浮点类型是 f64,因为在现代的 CPU 中它的速度与 f32 的几乎相同,但精度更高。所有浮点型都是有符号的。
2.1.3 数字运算
Rust 的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取模运算。整数除法会向下取整。
2.1.4 布尔类型
和大多数编程语言一样,Rust 中的布尔类型也有两个可能的值:true 和 false。布尔值的大小为 1 个字节。Rust 中的布尔类型使用 bool 声明。
2.1.5 字符类型
Rust 的 char(字符)类型是该语言最基本的字母类型,
char 字面量采用单引号括起来,这与字符串字面量不同,字符串字面量是用双引号括起来。Rust 的字符类型大小为 4 个字节,表示的是一个 Unicode 标量值,这意味着它可以表示的远远不止是 ASCII。标音字母,中文/日文/韩文的文字,emoji,还有零宽空格(zero width space)在 Rust 中都是合法的字符类型。Unicode 值的范围为 U+0000 ~ U+D7FF 和 U+E000~U+10FFFF。
2.2 复合类型
复合类型(compound type)可以将多个值组合成一个类型。Rust 有两种基本的复合类型:元组(tuple)和数组(array)。
2.2.1 元组类型
元组是将多种类型的多个值组合到一个复合类型中的一种基本方式。元组的长度是固定的:声明后,他们将无法增大或缩小。
我们通过在小括号内写入以逗号分隔的值列表来创建一个元组。
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。 这过程称为解构(destructuring),因为它将单个元组分为三部分。最后,程序打印出 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;
}
没有任何值的元组 () 是一种特殊的类型,只有一个值,也写成 ()。该类型被称为单元类型(unit type),该值被称为单元值(unit value)。如果表达式不返回任何其他值,就隐式地返回单元值。
2.2.2 数组类型
元组不同,数组的每个元素必须具有相同的类型。与某些其他语言中的数组不同,Rust 中的数组具有固定长度。
fn main() {
let a = [1, 2, 3, 4, 5];
}
使用方括号编写数组的类型,其中包含每个元素的类型、分号,然后是数组中的元素数,如下所示:
fn main() {
let a: [i32; 5] = [1, 2, 3, 4, 5];
}
以这种方式编写数组的类型看起来类似于初始化数组的另一种语法:如果要为每个元素创建包含相同值的数组,可以指定初始值,后跟分号,然后在方括号中指定数组的长度,如下所示:
let a = [3; 5]; // 等同于let a = [3, 3, 3, 3, 3];
注:如果运行时访问数组越界,Rust可以检测出来,并报panic
3. 函数
Rust 代码中的函数和变量名使用下划线命名法(snake case,直译为蛇形命名法)规范风格。在下划线命名法中,所有字母都是小写并使用下划线分隔单词。
3.1 语句和表达式
函数体由一系列语句组成,也可选择以表达式结尾。目前为止,我们介绍的函数还没有包含结尾表达式,不过你已经看到了表达式作为语句的一部分。因为 Rust 是一门基于表达式(expression-based)的语言,所以这是一个需要理解的重要区别。其他语言没有这样的区别,所以让我们看看语句和表达式分别是什么,以及它们的区别如何影响函数体。
语句(statement)是执行一些操作但不返回值的指令。表达式(expression)计算并产生一个值。
fn main() {
let y = 6; // 语句
let x = (let y = 6); // 表达式,这里是错误的,因为语句不返回值
}
函数定义也是语句,上面整个例子本身就是一个语句。用来创建新作用域的大括号(代码块) {} 也是一个表达式,例如:
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y); // y的值为4
}
这个表达式
{
let x = 3;
x + 1
}
是一个代码块,在这个例子中计算结果是 4。这个值作为 let 语句的一部分被绑定到 y 上。注意,x + 1 行的末尾没有分号,这与你目前见过的大部分代码行不同。表达式的结尾没有分号。如果在表达式的末尾加上分号,那么它就转换为语句,而语句不会返回值。在接下来探讨函数返回值和表达式时,请记住这一点。
3.2 带有返回值的函数
函数可以向调用它的代码返回值。我们并不会对返回值命名,但要在箭头(->
)后声明它的类型。在Rust中,函数的返回值等同于函数体最后一个表达式的值。使用return关键字和指定值,可以从函数中提前返回;但大部分函数隐式返回最后一个表达式。
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x); // x=6
}
fn plus_one(x: i32) -> i32 {
x + 1 // 没有分号,这里是一个表达式
}
4. 控制流
Rust 代码中最常见的用来控制执行流的结构是 if 表达式和循环。
4.1 if表达式
一个例子如下
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
所有的 if 表达式都以 if 关键字开头,其后跟一个条件。在这个例子中,条件检查变量 number 的值是否小于 5。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。if 表达式中与条件关联的代码块有时被叫做分支(arm)
fn main() {
let number = 3;
if number {
println!("number was three");
}
}
上面的代码将在编译时报错,因为rust不能像大多语言那样,会自动地将非布尔值转换为布尔值。必须自始至终地使用布尔值作为if的条件。
Rust也可以使用else if处理多重条件。
因为if是个表达式,所以也可以在let语句中使用if
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {}", number); // number=5
}
4.2 循环
Rust 有三种循环:loop、while 和 for。
4.2.1 使用loop重复执行代码
loop 关键字告诉 Rust 一遍又一遍地执行一段代码直到你明确要求停止。
可以使用 break 关键字来告诉程序何时停止循环。
循环中的 continue 关键字告诉程序跳过这个循环迭代中的任何剩余代码,并转到下一个迭代。
如果存在嵌套循环,break 和 continue 应用于此时最内层的循环。你可以选择在一个循环上指定一个循环标签(loop label),然后将标签与 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 run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
loop 的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果从循环中传递给其它的代码。为此,你可以在用于停止循环的 break 表达式添加你想要返回的值;该值将从循环中返回,以便您可以使用它,如下所示:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
当相等时,使用 break 关键字返回值 counter * 2。循环之后,我们通过分号结束赋值给 result 的语句。
4.2.2 while条件循环
在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用 break 停止循环。
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
}
4.2.3 使用for遍历集合
使用 for 循环来对一个集合的每个元素执行一些代码。
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {}", element);
}
}
for 循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时,例如示例 3-3 中使用 while 循环的倒计时例子,大部分 Rustacean 也会使用 for 循环。这么做的方式是使用 Range,它是标准库提供的类型,用来生成从一个数字开始到另一个数字之前结束的所有数字的序列。
下面是一个使用 for 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,rev,用来反转区间(range):
fn main() {
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
评论 (0)