Rust基础学习

变量绑定与解构

变量绑定:let a = "hello word"
不可变变量声明:let a = "hello word"
可变变量声明:let mut x =5
未使用变量声明:let _y = 6
变量解构:let (a, mut b): (bool, bool) = (true, false)
变量遮蔽(shadowing):在后面声明的变量会遮蔽掉前面声明的,如下:

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let x = 5;
// 在main函数的作用域内对之前的x进行遮蔽
let x = x + 1;

{
// 在当前花括号的作用域内,对之前的x进行遮蔽
let x = x * 2;

}
}

简而言之,变量遮蔽就是在不同的作用域中可以声明相同变量名的变量。

基本数据类型

无外乎那么几种类型,但是注意,在Rust语言中,我们需要显式地基于变量一个类型,否则编译器将自动推导。
这里整理一份基本类型的表:

数值类型

整数类型

长度 有符号类型 无符号类型
8 i8 u8
16 i16 u16
32 i32 u32
64 i64 u64
128 i128 u128
视架构而定 isize usize

类型定义的统一形式为:有无符号+类型大小,无符号数(unsigned)表示数字只能取0和正数,范围在0~2^n-1,有符号(integer)标识数字可取正数、负数和0,范围在-(2^(n-1)~2^(n-1)-1)
并且,在rust中规定的字面量表示方法:

数字字面量 示例
十进制 98_222
十六进制 0xff
八进制 0o77
二进制 0b1111_0000
字节(仅限于u8) b’A’

rust整型默认使用i32
显式处理可能出现整型溢出的方法:

  • 使用wrapping_*方法在所有模式下都按照补码循环溢出的规则处理。
  • 如果使用checked_*方法是发生溢出,则返回None
  • 使用overflowing_*方法返回该值和一个指示是否存在溢出的布尔值。
  • 使用saturating_*方法,可以限定计算后的结果不超过目标类型的最大值或低于最小值。

浮点类型

浮点类型,zairust中使用(单精度浮点型)f32和(双精度浮点型)f64来表示,默认浮点类型为f64

浮点数陷阱
浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:

  1. 浮点数往往是一个确切数字的近似表达,浮点数类型是基于二进制实现的,但是计算的数字往往是基于十进制的,例如0,1在二进制上并不存在精确的表达形式,但是在十进制上就存在。这种不匹配性导致一定的歧义性,更多的,虽然浮点数能代表真实的数值,但是由于底层格式问题,它往往受限于定长的浮点数精度,如果想要表达完全精准的真实数字,只有使用无限精度的浮点数才行。
  2. 浮点数在某些特性上是反直觉的 例如大家都会觉得浮点数可以进行比较,一般情况下,确实可以使用< > => = <=进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为f32,f64上的比较运算实现的是std::cmp::PartialEq特征(类似其他语言的接口),但是并没有实现std::cmp::Eq特征,但是后者在其他数值类型上都有定义。比如说:
    RustHashMap数据结构,是一个KV类型的Hash Map实现,它对于k没有特定类型的限制,但是要求能用作k的类型必需实现了std::cmp::Eq特征,因此这意味着你无法使用浮点数作为HashMapkey,来存储键值对,但是作为对比,Rust的整数类型、字符串类型、布尔类型都实现了该特征,因此可以作为HashMapkey
    为了避免上面说的两个陷阱,你需要遵守以下准则:
  • 避免在浮点数上测试相等性。
  • 当结果在数学上可能存在未定义时,需要格外的小心。

Rust的浮点数类型使用NaN(not a number)来处理数学上未定义的结果。并且所有跟NaN交互的操作,都会返回一个NaN,而且NaN不能用来比较。并且,可以使用is_nan()等方法,来判断一个数值是否是NaN

数学运算
来看一个综合性的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
fn main() {
// 编译器会进行自动推导,给予twenty i32的类型
let twenty = 20;
// 显式类型标注
let twenty_one: i32 = 21;
//通过类型后缀的方式进行类型标注
let twenty_two = 22i32;

// 只有同样的类型,才能进行运算
let addition = twenty + twenty_one + twenty_two;
println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);

// 对于较长的数字,可以使用_进行分割,提高可读性
let one_million: i64 = 1_000_000;
println!("{}", ont_million.pow(2));

// 定义一个f32数组,其中42.0会自动被推导为f32类型
let forty_twos = [
42.0,
42f32,
42.0_f32,
];

// 打印数组中的第一个值,并控制小数位为2位
println!("{:.2}", forty_twos[0]);
}

位运算

与其他语言类似

运算符 说明
&位与 相同位置均为1时则为1,否则为0
|位或 相同位置只要有1则为1,否则为0
^异或 相同位置不相同则为1, 相同则为0
!位非 把位中的0和1相互取反,即0置为1,1置为0
<<左移 所有位向左移动指定位数,右位补0
>>右移 所有位向右移动指定位数,带符号移动(正数补0,负数补1)