Rust中通用的编程概念
在这篇文章中,我们将学习一些在 Rust 中通用的编程概念:
- 变量与可变性
- 数据类型
- 标量类型
- 复合类型
- 函数
- 控制流
变量与可变性
变量时不可变的
在 Rust 中,我们通过关键字 let
来声明变量。默认情况下,变量处于不可变(immutable)的状态。
这里的不可变意思是,当我们通过赋值操作,将一个值绑定到了一个变量上后,这个变量就不可以被重新赋值了。
如果我们需要对这个变量重新赋值,则需要在声明变量的同时,声明变量为可变(mutable)的,声明的方式为在变量名前加上 mut
关键字。
下面我们来看一个例子:
1 |
|
在上面的代码中,我们声明了变量 b
为 mut
,也就是可变的,因此我们才能在声明并赋值变量后,再通过赋值语句 b = 1;
将其绑定的值修改为 1
。
而对于变量 a
,我们没有为其声明可变。如果我们在后面强行加上 a = 1;
的话,编译器则会报错,编译无法通过。
变量与常量
虽然在 Rust 中,变量默认是不可变的,但是请不要将它与常量的概念进行混淆。在 Rust 中,是有专门的叫做常量(constant)的概念的。
常量在绑定值后也是不可变的,但是它和不可变变量也有很多区别:
- 首先,声明常量时不能加上
mut
,无论在何种情况下,常量都是不能改变的。 - 声明常量需要使用
const
关键字,同时其类型必须在声明常量时显式标注。 - 常量可以在任意作用域进行声明,包括全局作用域。
- 常量只可以绑定到常量表达式,无法绑定到函数的调用结果或是只能在运行时才能计算出的值。
在 Rust 中,常量的命名规范是 全部使用大写字母,单词之间以下划线分割开来,比如:
- APP_VERSION
- MAX_SCORE
隐藏 - Shadowing
在 Rust 中,Shadow 是一个比较重要的概念。
我们可以使用相同的名字声明新的变量,新的变量就会 shadow 掉之前声明的同名变量。
我们来看下面这个例子:
1 |
|
我们前面说过,一个变量声明后默认是不可变的。如果我们在声明 let x = 0;
之后,通过语句 x = x + 1
重新为其赋值是不符合 Rust 的语法规则的。
但是在这段代码中,我们通过 let x = x + 1;
重新声明了一个 x
,并通过这个新的声明覆盖(隐藏)掉了最开始声明的变量 x
。后续我们再使用这个变量则会指向我们最新声明的 x
。
但是需要注意的是,shadow 这个行为与声明变量为 mut
是不一样的。
不管新旧变量,如果没用 mut
进行标记,则都是不可变的;同时,通过 shadow 声明的新变量,它的类型可以与之前的不同。
数据类型
Rust 是一门静态类型语言,在编译时必须知道所有变量的类型。基于使用的值,编译器通常能够自行推断出变量的具体类型。但是,如果可能的类型比较多,就必须添加显式的类型标注,否则编译会报错。
类型的声明方式为在声明变量时,在变量名后面加上英文冒号。冒号后面写上声明的类型。比如 let x: u32 = 0;
。
标量类型
一个标量类型代表一个单个的值。Rust 有四个标量类型:
- 整数类型
- 浮点类型
- 布尔类型
- 字符类型
整数类型
整数类型是没有小数部分的,类型通常以 u/i
开头。其中 u 表示无符号整数类型,i 表示有符号整数类型。
比如 u32
就是无符号32位整数类型,i8
就是有符号8位整数类型。
同时,还有 usize
与 isize
类型,它们的位宽与程序运行时的计算机架构所决定的,如果是64位计算机,那就是64位的。
浮点类型
在 Rust 中,有两种基础的浮点类型,分别是 f32
和 f64
。
- f32,32位,单精度浮点类型
- f64,64位,双精度浮点类型
Rust 的浮点类型使用 IEEE-754 标准进行表述。
布尔类型
Rust 的布尔类型也具有 true
和 false
两个值。占据一个字节大小。
字符类型
Rust 语言中,char 类型被用来描述语言中最基础的单个字符。
字符类型的字面值使用单引号。
char 类型占据四个字节大小,是 Unicode 标量值。
复合类型
复合类型可以将多个值放在一个类型里。
Rust 提供了两种基础的复合类型:元组(tuple)和数组。
元组
Tuple 可以将多个类型的多个值放在一个类型里。Tuple 的长度是固定的,一旦声明就无法改变其长度。
创建 Tuple 只需要将多个值写在小括号里,每个值用逗号分开:
1 |
|
除了上面的例子中给出的根据索引号获取 tuple 中对应值的方法外,我们还可以通过模式匹配来解构(destructure) tuple 中的值,比如上面的代码我们也可以写成下面这种样子:
1 |
|
数组
数组也可以将多个值放在一个类型中,但是它只能存放类型相同的元素,同时其长度也是固定的。声明数组时,只需要像这样通过中括号括起来所有需要的元素即可:let arr = [1, 2, 3];
。
如果要对数组进行类型声明,则应当采用这种形式 —— **[类型; 长度]**。
例如:let arr: [u8; 3] = [1, 2, 3];
。
如果数组内所有元素值都相同,那么可以通过下面的方式进行声明:
1 |
|
函数
接下来我们介绍一下 Rust 中的函数。
在 Rust 中,声明函数需要用到 fn
关键字,比如 fn callback() {}
。同时,和对变量命名的规范相同,函数的命名也使用 snake case 命名规范,所有的字母都是小写的,单词之间使用下划线(_
)进行连接。
函数的参数
在 Rust 中,必须为函数的签名里的每个变量声明类型,就像下面这样:
1 |
|
语句和表达式
Rust 的函数体由一系列的语句组成,可以由一个表达式结束(也可以不以表达式结束)。
- 语句:语句是执行一些动作的指令
let x = 6; let y = 7;
- 表达式:表达式会运算得到一个值
x + y
函数的定义也是一个语句。语句没有返回值,所以我们不可以用 let
将一个语句赋给一个变量。
1 |
|
函数的返回值
在 Rust 中声明的函数如果存在返回值,需要通过符号 ->
在函数参数后声明返回值类型。返回值就是函数体里最后一个表达式的值,或第一个通过 return
关键字返回的值。
1 |
|
控制流
if 表达式
if 表达式允许您根据条件来执行不同的代码分支,这个条件必须是 bool 类型。
if 表达式中,与条件相关联的代码块被称作**分支(arm)**,可以在后面加上 else
表达式。
1 |
|
因为 if 是一种表达式,所以可以使用 let
将 if 赋值给变量。在 Rust 中,不存在三目运算符,因此无法像编写 JavaScript 那样使用方便的三目运算:
1 |
|
但是通过 if 表达式,我们就可以巧妙地实现三目运算符的效果:
1 |
|
Rust 的循环
在 Rust 中,提供了三种循环类型:loop
、while
、for
。
loop 循环
loop
关键字告诉 Rust 反复执行一段代码,直到你主动跳出循环,实际效果类似 while(true)
。
1 |
|
while 循环
while 循环的写法与其他主流的高级语言写法基本类似,在 while
关键字后写上条件表达式,当表达式为 false
时跳出循环:
1 |
|
for 循环
在 Rust 中,for 循环一般被用来遍历集合体。
1 |
|
除了集合体,我们也可以通过标准库提供的 Range
类型执行指定次数的 for 循环。声明一个 Range 只需要指定起始的数字和结束的数字,Range会生成 [起始数字, 结束数字)
区间内的数字集合。同时,通过 rev
方法可以反转 Range
。
1 |
|