Rust 代码中的函数和变量名使用下划线命名法(*snake case*,直译为蛇形命名法)规范风格。
当变量不可变时,这意味着一旦一个值绑定到一个变量名后,就不能更改该值了。
let x = 5; // 不可变
x = 6; // 这里会报错
let mut x = 5;
x = 6; // 这里可以完成赋值
Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词。
与不可变变量类似,常量(*constant*)是绑定到一个常量名且不允许更改的值。常量不允许使用 mut
。常量使用 const
关键字而不是 let
关键字来声明,并且值的类型必须注明。
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
可以声明和前面变量具有相同名称的新变量。第一个变量被第二个变量 遮蔽(*shadow*),这意味着当我们使用变量时我们看到的会是第二个变量的值。我们可以通过使用相同的变量名并重复使用 let
关键字来遮蔽变量。常量在程序运行的整个过程中都有效。
mut
和遮蔽之间的另一个区别是,因为我们在再次使用 let
关键字时有效地创建了一个新的变量,所以我们可以改变值的类型(使用 mut
的话变量值类型不可变),但重复使用相同的名称。
fn main() {
let x = 5;
let x = x + 1; // x = 6
{// 这是一个作用域
let x = x * 2;
println!("The value of x in the inner scope is: {}", x);// x = 12
}
println!("The value of x is: {}", x); // x = 6
}
标量(*scalar*)类型表示单个值。Rust 有 4 个基本的标量类型:整型、浮点型、布尔型和字符。
长度 | 有符号类型 | 无符号类型 |
---|---|---|
8 位 | i8 |
u8 |
16 位 | i16 |
u16 |
32 位 | i32 |
u32 |
64 位 | i64 |
u64 |
128 位 | i128 |
u128 |
arch | isize |
usize |
isize
和 usize
类型取决于程序运行的计算机体系结构,在表中表示为“arch”:若使用 64 位架构系统则为 64 位,若使用 32 位架构系统则为 32 位。
比方说有一个 u8
,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生整型溢出(*integer overflow*),这会导致两种行为的其中一种。
--release
参数进行发布(release)模式构建时,Rust 不检测会导致 panic 的整型溢出。相反当检测到整型溢出时,Rust 会进行一种被称为二进制补码包裹(*two’s complement wrapping*)的操作。比如在 u8
的情况下,256 变成 0,257 变成 1,依此类推。浮点数(*floating-point number*)是带有小数点的数字,在 Rust 中浮点类型(简称浮点型)数字也有两种基本类型。Rust 的浮点型是 f32
和 f64
,它们的大小分别为 32 位和 64 位。默认浮点类型是 f64
,因为在现代的 CPU 中它的速度与 f32
的几乎相同,但精度更高。所有浮点型都是有符号的。
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
Rust 的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取模运算。整数除法会向下取整。
fn main() {
// addition 加
let sum = 5 + 10; // 15
// subtraction 减
let difference = 95.5 - 4.3; // 91.2
// multiplication 乘
let product = 4 * 30; // 120
// division 除
let quotient = 56.7 / 32.2; // 1.76
let floored = 2 / 3; // 0
// remainder 取余
let remainder = 43 % 5; // 3
}
和大多数编程语言一样,Rust 中的布尔类型也有两个可能的值:true
和 false
。布尔值的大小为 1 个字节。Rust 中的布尔类型使用 bool
声明。
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}
Rust 的 char
(字符)类型是该语言最基本的字母类型。注意,我们声明的 char
字面量采用单引号括起来,这和字符串字面不同,字符串字面量是用双引号扩起来。Rust 的字符类型大小为 4 个字节,表示的是一个 Unicode
标量值,这意味着它可以表示的远远不止是 ASCII。标音字母,中文/日文/韩文的文字,emoji,还有零宽空格(zero width space)在 Rust 中都是合法的字符类型。
fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '?';
}
元组是将多种类型的多个值组合到一个复合类型中的一种基本方式。元组的长度是固定的:声明后,它们就无法增长或缩小。
我们通过在小括号内写入以逗号分隔的值列表来创建一个元组。元组中的每个位置都有一个类型,并且元组中不同值的类型不要求是相同的。
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
变量 tup
绑定到整个元组,因为元组被认作是单个复合元素。 想从元组中获取个别值,我们可以使用模式匹配来解构(destructure)元组的一个值
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y); // 6.4
}
除了通过模式匹配进行解构外,我们还可以使用一个句点(.
)连上要访问的值的索引来直接访问元组元素。
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0; // 500
let six_point_four = x.1; // 6.4
let one = x.2; // 1
}
没有任何值的元组 ()
是一种特殊的类型,只有一个值,也写成 ()
。该类型被称为单元类型(*unit type*),该值被称为单元值(*unit value*)。如果表达式不返回任何其他值,就隐式地返回单元值。
fn main() {
let a = guess();
if a == () {
println!("a==()")
}
}
fn guess(){}
将多个值组合在一起的另一种方式就是使用数组(*array*)。与元组不同,数组的每个元素必须具有相同的类型。Rust 中的数组具有固定长度。
当你希望将数据分配到栈(stack)而不是堆(heap)时,或者当你希望确保始终具有固定数量的元素时,数组特别有用。
fn main() {
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5]; // 等价于 let a = [3, 3, 3, 3, 3];
}
数组是可以在栈上分配的已知固定大小的单个内存块。可以使用索引访问数组的元素
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0]; // 1
let second = a[1]; // 2
}
如果尝试访问超出数组末尾的数组元素,将导致运行时(*runtime*)错误。程序退出并显示错误消息。
Rust 代码中的函数和变量名使用下划线命名法(*snake case*,直译为蛇形命名法)规范风格。
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
main
函数中的代码会按顺序执行。首先,打印 “Hello, world!” 信息,然后调用 another_function
函数并打印它的信息。
函数也可以被定义为拥有参数(*parameter*),参数是特殊变量,是函数签名的一部分。当函数拥有参数(形参)时,可以为这些参数提供具体的值(实参)。技术上讲,这些具体值被称为实参(*argument*),但是在日常交流中,人们倾向于不区分使用 parameter 和 argument 来表示函数定义中的变量或调用函数时传入的具体值。
fn main() {
another_function(5);// 实参 argument
}
fn another_function(x: i32) {// 形参 parameter
println!("The value of x is: {}", x);
}
函数体由一系列语句组成,也可选地以表达式结尾。表达式是语句的一部分。因为 Rust 是一门基于表达式(expression-based)的语言,所以这是一个需要理解的 重要区别 。
语句(*statement*)是执行一些操作但不返回值的指令。
表达式(*expression*)计算并产生一个值。
fn main() {
let y = 6; // 这是一条语句 , 6 是表达式
let y = {
let x = 3;
x + 1 // 这是表达式
};
println!("The value of y is: {}", y); // 4
}
表达式 的结尾 没有分号。
如果在表达式的末尾加上 分号,那么它就转换为 语句,而语句没有返回值。
函数可以向调用它的代码返回值。我们并不对返回值命名,但要在箭头(->
)后声明它的类型。在 Rust 中,函数的 返回值等同于函数体最后一个表达式的值。使用 return
关键字和指定值,可从函数中提前返回;但大部分函数 隐式的 返回 最后的表达式。
fn five() -> i32 {
5
}
fn main() {
let x = five(); // 5
println!("The value of x is: {}", x); // 5
let x = plus_one(5); // 报错
}
fn plus_one(x: i32) -> i32 {
x + 1; // 已分号结尾是语句 则没有返回值
// 这里会报错
// |
//7 | fn plus_one(x: i32) -> i32 {
// | -------- ^^^ expected `i32`, found `()`
// | |
// | implicitly returns `()` as its body has no tail or `return` expression
//8 | x + 1;
}
主要的错误信息 “mismatched types”(类型不匹配)揭示了这段代码的核心问题。函数 plus_one
的定义说明它要返回一个 i32
类型的值,不过语句并不会返回值,此值由单位类型 ()
表示,表示不返回值。因为不返回值与函数定义相矛盾,从而出现一个错误。
根据条件是否为真来决定是否执行某些代码,或根据条件是否为真来重复运行一段代码,是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 if
表达式和循环。
if
表达式所有的 if
表达式都以 if
关键字开头,其后跟一个条件。
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
let
语句中使用 if
因为 if
是一个表达式,我们可以在 let
语句的右侧使用它来将结果赋值给一个变量。
整个 if
表达式的值取决于哪个代码块被执行。这意味着 if
的每个分支的可能的返回值都必须是相同类型。
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {}", number);
}
Rust 有三种循环:loop
、while
和 for
。
loop
关键字告诉 Rust 一遍又一遍地执行一段代码直到你明确要求停止。如:使用 break
关键字来告诉程序何时停止循环。
循环中的 continue
关键字告诉程序跳过这个循环迭代中的任何剩余代码,并转到下一个迭代。
fn main() {
loop {
println!("again!");
}
}
如果存在嵌套循环,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);
}
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 来存放循环的返回值。在循环的每一次迭代中,我们将 counter 变量加 1,接着检查计数是否等于 10。当相等时,使用 break 关键字返回值 counter * 2。循环之后,我们通过分号结束赋值给 result 的语句。最后打印出 result 的值,也就是 20。
当条件为真,执行循环。当条件不再为真,调用 break
停止循环。这个模式太常用了,Rust 为此内置了一个语言结构,它被称为 while
循环。
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
}
for
遍历集合for
循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时。
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {}", element);
}
}
大部分 Rustacean 也会使用 for
循环。这么做的方式是使用 Range
,它是标准库提供的类型,用来生成从一个数字开始到另一个数字之前结束的所有数字的序列。
fn main() {
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
好好学习,天天向上