变量与可变性
Rust变量是默认不可变的。一旦它被绑定到了某个值上面,这个值就无法再被改变。
要想变量可变,需要在声明的变量的时候在变量前面添加mut关键字来实现。
对于重型数据结构,适当使用可变性去修改一个实例,要比赋值或者重新返回一个新实例要更有效率,对于轻量的数据结构,通过函数式编程创建新变量来赋值,代码更容易理解。
变量与常量的不同
虽然Rust中变量默认是不可变的,但是它与常量仍然是不同的。
- 常量不能使用
mut修饰 - 常量使用
const修饰,并且声明时要显示标注类型。 - 常量可以被声明在任何作用域上。
- 常量只能绑定到常量表达式上,编译期就能够确定值。
Rust确定使用下划线分割的全大写字母来命名一个常量。
隐藏
我们知道在C/C++中是不可以重复声明变量的,但是Rust中可以。
重复使用let关键字配以相同的名称,那么在接下来的生命周期中,发挥作用的就是新的变量,旧的变量就好像被隐藏(shadow) 一样。
隐藏机制实际上是创建了一个新的变量,可以修改可变性和变量的类型。
常见的使用实例就是:从控制台读取的输入都是字符串类型,但是通常需要转换成我们需要的类型,使用隐藏就可以方便实现这一点而不用引入新的命名。
隐藏机制可以在保持不可变性的情况下对变量进行变换。
数据类型
Rust是一种静态类型语言,需要在编译程序的过程中就需要知道所有变量的具体类型。
大部分情况下,编译器可以根据如何绑定、使用变量的值来自动推导出类型,对于不能推导出的,需要显示标注类型。
标量类型
标量类型是单个值的类型的统称。Rust中内建了4种基本的标量类型:整数、浮点数、布尔值及字符。
整数类型
整数是指没有小数部分的数字。
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8位 | i8 | u8 |
| 16位 | i16 | u16 |
| 32位 | i32 | u32 |
| 64位 | i64 | u64 |
| arch | isize | usize |
isize和usize比较特殊,它们的长度是与平台架构相关的,64位架构就是64位、32位架构就是32位。
整数字面量如下:
| 整数字面量 | 示例 |
|---|---|
| Decimal | 98_222 |
| Hex | 0xff |
| Octal | 0o77 |
| Binary | 0b1111_0000 |
| Byte(u8) | b’A’ |
Rust默认整数字面量的推导类型为i32,它在大部分情况下都是运行最快的,64位系统也是如此。
usize和isize通常用作某些集合的索引。
整数溢出
在debug模式下,程序中包含整数溢出的运行时检测代码,检测到溢出会触发panic。 在release模式下,整数溢出不会触发panic
浮点数类型
浮点数就是带小数的数字。只有两种类型f32和f64,分别占用32位和64位。
默认的浮点数字字面量会推导为f64,精度更高并且运行效率相差不大。
数值运算
数值类型都支持常见的数学运算:加、间、乘、除、取余。
布尔类型
布尔类型只有两个值:true和false,占用一个字节空间。
字符类型
Rust中使用char描述单个字符,使用单引号指定。
char类型占用4字节,一个Unicode标量值。甚至emoji表情就是一个有效的char类型。
复合类型
复合类型可以将多个不同类型的值组合成为一个类型。Rust有两种内置的基础复合类型:元组(tuple)和数组(array)。
元组类型
元组可以将多个不同类型的值组合在一起,并且在元组声明结束后长度就固定了,无法增加或者减少。 示例:
fn main() {
// 声明一个元素
let tup = (500, 6.4, 1);
// 使用模式匹配解构元素
let (x,y,z) = tup;
println!("The value of y is {}", y)
}使用.和索引来访问元组的元素,例如:
let tup = (500, 6.4, 1);
let num1 = tup.0
let num2 = tup.2数组类型
数组中的每个元素必须是相同的类型,Rust数组与其它语言的不同,Rust中的数组一旦声明,大小固定不可改变。对于需要改变大小的数组,Rust提供了动态数组vector,这在后续介绍。
示例:
// 创建一个类型为i32,长度为5的数组
let a:[i32; 5] = [1,2,3,4,5];
// 创建一个数组为[3,3,3,3,3]
let b = [3;5]
// 访问数组元素
a[0];
a[1]一般对于长度固定的数据,例如月份,会使用数组类型。其它情况使用动态数组。
使用非法索引访问数组,Rust将会触发panic。
函数
Rust函数的格式如下:
fn 函数名(参数列表) -> 返回类型{
//函数体
}- 使用
fn关键字来声明一个函数 - Rust使用蛇形命名法来规范函数和变量的风格,就是使用全小写和下划线分割单词的格式命名。
- 参数列表中的参数使用逗号分割,每个参数必须显示标注类型。
- Rust函数可以使用元组包含多个值来实现多个返回值的。
语句和表达式
Rust是基于表达式的语言。将语句和表达式分为两个概念。
- 语句:执行操作但不返回值的指令。
- 表达式:进行计算并产生一个值作为结果的指令。
例如,let创建变量绑定值就是一条语句,因为没有值产生,所以无法将let语句赋值给一个变量
// 发生错误
let x = (let y = 6)所以在Rust中无法实现其它语言一样的x = y = 6这样的语句。
能够返回值的就是表达式,无论是字面量还是数值运算,例如
let x = 5;
let y = {
let x = 3;
x + 1
}在Rust中,绝大部分代码都是表达式,例如5 + 6,而表达式也常作为语句的一部分,例如上面的字面量5,函数调用也是表达式,创建新作用于的花括号也是表达式。
代码块中的最后一条表达式的值就是代码块的返回值,注意上面的x + 1后面没有分号,计算的结果是4,代码块的返回结果也是4,最后绑定到变量y上的值也是4。
函数返回值
在Rust中,函数的返回值等同于函数体最后一个表达式的值。这其实与上面说所的一致,因为函数体也是在代码块当中的。
不过也可以使用return关键字指定一个值提前从返回中返回。
Note
Rust中的函数和代码块的最后一条指令如果是语句,因为语句没有值,而代码块实际上是一个有返回值的表达式,所以Rust中函数或代码块默认会返回
()空元组。
注释
Rust中的注释以双斜杠//开始,一直到本行结束
// Hello, World控制流
主要就是if表达式和循环表达式。
if表达式
满足条件则执行这代码,不满足则跳过。
fn main() {
let number = 2;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}Note
条件表达式必须产生一个bool类型的值,而不像C语言一样能够将非0值作为true,0值作为false
可以使用else if来实现多重条件判断。
从本节标题就可以看出来,if表达式既然是表达式,那么是可以产生值的,也就是说我们可以将if表达式的值绑定到变量上。
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is {}", number);
}if表达式的值取决于哪个分支得到了执行,也就是说,每个分支可能的返回值类型是同一类型。
循环表达式
Rust有三种循环:loop、while和for。与if一样,作为表达式是同样会产生值的。
三种循环是可以相互转换的。
loop循环
loop循环反复执行一块代码,直到显示声明退出。
fn main() {
loop {
println!("again!");
}
}loop可以使用break退出循环,还可以在break后面添加表达式作为循环的返回值
fn main() {
let mut counter = 0;
loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
}
}while循环
while循环每次执行循环体之前都判断一次条件。条件为真再执行。
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number = number - 1;
}
}for循环
for循环通常用来遍历集合,不需要像loop或者while循环一样关注索引,避免不正确的索引导致错误出现。
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("The value is {}!", element);
}
}for循环是最常用的循环结构,通常与Range配置来实现特定次数的任务:
fn main() {
for number in (1..4) {
println!("{}!", number);
}
}Range就是上面的(1..4)的写法,用来生成从一个数字开始到另一个数字结束之前的所有数字序列。