1. 变量
- 变量 : 声明时用
let
关键字,默认不可变,但可再增加mut
声明成可变 - 常量 : 声明时用
const
关键字,且必须标注类型,可以声明在任何作用域里。只能绑定常量表达式。规范是全部大写,且用下划线连接多个单词。
变量特点:
- 变量绑定和所有权:使用
变量绑定
来代替变量赋值
,是因为 Rust 中,变量是会有所有权交换的 - 变量不可变:默认情况下变量是不可变的,一旦为它绑定值,就不能再修改
- 变量忽略:当创建变量而未使用时,Rust 会提出警告,可以用下划线作为变量名的开头来取消警告
- 变量解构:可以使用
let
来进行复杂变量解构,只取出一部分内容,并可以使用元组、切片、结构体给解构式赋值 - 变量遮蔽:可以使用相同名字声明新变量,此时会将之前的声明覆盖掉
1 | // 变量不可变:如下,cargo run 会报错,cannot assign twice to immutable variable y |
2. 所有权和借用
通过所有权来管理内存,编译器在编译时会根据一系列规则进行检查,因此对于程序运行期,不会有任何性能上的损失。
所有权原则:
- Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者
- 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
- 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)
堆和栈:
- 栈:后进先出,所有数据都必须占用已知且固定大小的内存空间
- 堆:存储大小未知或者可能变化的数据,当堆上放入数据时,需要请求一定大小的内存空间,并返回一个表示该位置地址的指针,,该指针会被推入栈中
1. 转移所有权
对于基本数据类型,是通过自动拷贝的方式来赋值的,都被存在栈中。
1 | let x = 5; |
复杂类型,由存储在栈中的 堆指针、字符串长度、字符串容量
组成,为了提高性能,拷贝时只会字面量本身(浅拷贝),如下方式,就会违背 “一个值只允许有一个所有者” 的原则,可能导致 “二次释放” 问题,因此 Rust 中,解决方法是:当 s1 被赋予 s2 后,所有权从 s1 转移到 s2,s1 马上失效。
1 | // 转移所有权:s1 会变成无效引用,使用时会报错 |
2. 引用与借用
仅通过转移所有权的方式获取一个值,会让程序变复杂,可以通过获取变量的引用简化操作,也叫做借用。常规引用是一个指针类型,指向了对象存储的内存地址。
1 | fn main() { |
借用规则:
- 同一时刻,只能拥有一个可变引用, 或者任意多个不可变引用
- 引用必须总是有效的
1 | // 不可变引用:引用指向的值默认不可变 |
1. 可变引用与不可变引用不能同时存在
同一作用域,特定数据只能有一个可变引用,但不可变的可以有多个,即借用规则第一条。
1 | let mut s = String::from("hello"); |
2. NLL(Non-Lexical Lifetimes)
它是一个 Rust 编译器优化行为,用于找到某个引用在作用域结束前就不再被使用的代码位置。
1 | fn main() { |
3. 悬垂引用(Dangling References)
悬垂引用也叫做悬垂指针,意为指针指向的值被释放了,而指针仍然存在,在 Rust 中编译器可以确保引用永远也不会变成悬垂状态。
3. 数据类型
作为静态语言,编译时需要知道其类型,当可能得类型比较多时(如字符串转数字),不指定类型就会编译报错。
1. 标量类型
标量(scalar)类型表示单个值,由以下组成:
- 整数类型:有无符号分为
i
和u
,长度为8,16,32,64,128bit
,特殊的有isize
和usize
,共有 12 种了类型i32
为默认类型isize
和usize
,根据系统架构决定,如 32 位计算机长度为 32bit- 可以使用如下任意形式来编写整型的字面量,如使用类型后缀来指定类型(57u8),使用
_
作为可视分隔符(1_000) - 对于整型溢出,当使用调试模式时,会抛出 panic,发布模式下则会直接环绕,如 u8 时,256变0,257变1
- 浮点类型:分为2种,
f32
单精度32位,f64
双精度64位,f64
为默认类型
- 布尔类型:符号为
bool
,占1个字节,分为 2 种,true
和false
- 字符类型:符号为
char
,占4个字节,字面量使用单引号,表示一个 Unicode 标量值
数字字面量 | 示例 |
---|---|
十进制 | 98_222 |
十六进制 | 0xff |
八进制 | 0o77 |
二进制 | 0b1111_0000 |
字节 | b’A’ |
2. 数字运算
1 | fn main() { |
3. 位运算
位运算符除了 !
之外都可以加上 =
进行赋值,因为 ! =
要用来判断不等于
运算符 | 说明 |
---|---|
& 位与 |
相同位置均为1时则为1,否则为0 |
\| 位或 |
相同位置只要有1时则为1,否则为0 |
^ 异或 |
相同位置不相同则为1,相同则为0 |
! 位非 |
把位中的0和1相互取反,即0置为1,1置为0 |
<< 左移 |
所有位向左移动指定位数,右位补0 |
>> 右移 |
所有位向右移动指定位数,带符号移动(正数补0,负数补1) |
1 | fn main() { |
2. 复合类型
1. Tuple 元组
可以存放多种数据类型,且长度是固定的,一旦声明就无法改变。
1 | // 创建 |
- 没有任何值的元组
()
是一种特殊的类型,被称为单元类型(unit type),它只有一个值()
,该值被称为单元值(unit value)。 - 如果表达式不返回任何其他值,就隐式地返回单元值
2. 数组
只能存放单一数据类型,且长度是固定的,存在栈内存中。当下标溢出时,编译通过,但运行时报错。
1 | // 创建: 可以显示声明类型或者省略 |