数据类型¶
一、概述¶
Rust的数据类型包含两个大类,即:标量和复合类型。Rust是静态语言,在编译时必须知道所有变量类型
-
基于使用的值,编译器通常能够推断出它的具体类型
-
但如果可能的类型比较多,就必须添加类型声明标注,否则会编译报错。例如把String调用parse方法转为整形,我们必须标注接受值的变量为整形,是因为该方法能推断的类型种类比较多,如:
i32
、u32
等。
我们可以使用以下示例代码声明一个整形变量,用于存储通过解析字符串得到的值。这样rust就知道我们要解析的值确定是u32
类型
fn main() {
let guess: u32 = "42".parse().expect("Not a number!");
println!("You guessed: {}", guess);
}
二、标量类型¶
-
一个标量类型代表一个单个的值
-
Rust有四个主要的标量类型
-
整数类型
-
浮点类型
-
布尔类型
-
字符类型
2.1 整数类型¶
整数类型 整数类型没有小数部分,例如u32就是一个无符号的整数类型,占据32位的空间。无符号整数类型以u开头,有符号的数据类型以i开发,rust的整数类型列表如下图
长度 | 有符号类型 | 无符号类型 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
有符号的数字类型有正有负,无符号的数字类型只有正数。
有符号范围:-(2^n - 1) 到 2^(n-1) - 1
无符号范围:2^n - 1
arch
类型由计算机的架构位数所决定,如果计算机是64位,那么isize
和usize
的位数就是64。使用isize
和usize
的主要场景是对某种集合进行索引操作。
整数字面值 整数的字面值,可以有多种表示形式,如下表
字面值 | 示例 |
---|---|
十进制(decimal) | 98_222 |
十六进制(hex) | 0xff |
八进制(Octal) | 0o77 |
二进制(Binary) | 0b1111_0000 |
字节(仅限与u8类型) | b'A' |
出了byte类型之外,所有的数值类型都允许使用类型后缀,例如:57u8
如果不清楚应该使用哪种类型,可以使用rust相应的默认类型,整数的默认类型是i32
,总体上来说速度很快,即使在64位系统中
2.1.1 整数溢出¶
例如:u8的范围是0-255,如果你把一个u8变量的值设为256,那么:
- 在调试模式下编译,rust会检查整数溢出,如果发生溢出,程序在运行时就会发生panic
- 在发布模式下(--release)编译,rust不会检查可能导致panic的整数溢出,如果发生溢出,rust会执行环绕操作,256变成0,257变成1,以此类推,但不会导致程序的panic
2.2 浮点类型¶
rust有两种基础的浮点类型,也就是含有小数部分的类型,包含以下两种
-
f32,占据32位长度,单精度
-
f64,占据64位长度,双精度
rust的浮点数类型使用了 IEEE-754 标注来描述,f64 是默认类型,因为在现代计算机里上f64和f32运行速度差不多,但f64更加精确。
2.3 数值的操作¶
- 数值支持加、减、乘、除、余计算,
2.3 布尔类型¶
rust的布尔类型也有两个值:true和false,占用一个字节的大小
2.4 字符类型¶
rust语言中char类型被用来描述语言中最基础的单个字符,字符类型的字面值使用单引号,占用4个字节的大小,字符类型是Unicode标量值,可以表示比 ASCII 多很多的字符内容:拼音、中文、日文、韩文、零长度空白字符、emoji表情等。范围值是
-
U+0000 到 U+D7FF
-
U+E000 到 U+10FFFF
但是,Unicode 中并没有“字符”概念,所以直觉上认为的字符也许与rust中的概念不相符。
三、复合类型¶
复合类型可以将多个值放在一个类型了,rust提供两种基础的复合类型:元组(tuple)、数组(数组)
3.1 元组¶
Tuple可以将多个类型的多个值放在一个类型里,Tuple的长度是固定的,一旦声明后就无法改变。创建Tuple的过程如下
-
在小括号里,将值用都好隔开
-
Tuple中的每个位置都对应一个类型,Tuple中各元素的类型不必相同
3.1.1 声明元组¶
以下创建一个Tuple类型数据,并将数据内容输出到终端上
3.1.2 获取tuple的元素值¶
可以使用模式匹配来结构(destructure)一个Tuple来获取元素的值。例子:
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of x, y z, is: {},{},{}", x, y, z);
}
3.1.3 访问tuple的元素¶
在rust中使用点标记法,后接元素索引号来访问tuple元素,如下
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
println!("The value of tup items is: {},{},{}", tup.0, tup.1, tup.2);
}
3.2 数组¶
数组也可以将多个值放在一个类型里,但数组中每个元素的类型必须相同,数组的长度也是固定的,一旦声明之后,不可以改变。
声明数组 在中括号里,各个值使用都好隔开,如下例子
以下再定义一个包含所有月份的数组,并打印第一个元素
fn main() {
let months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
print!("the first item is {}", months[0]);
}
3.2.2 数组的用处¶
如果想让数据存储在栈(stack)内存上,而不是堆(heap)内存上,或者想保证有固定数量的元素,这时使用数组更有号去处。
数组是由预导入模块(prelude)提供,不过prelude也是标准库的一部分,的可以说标准库式prelude的超集。
实际上rust标准库还提供了一个和数组类型的数据类型--vector,数组没有vector灵活,vector的长度可以改变。如果我们不确定使用数组还是使用vector,这时候应当使用vector。
3.2.3 数组的类型¶
数组的类型以及长度以下面这种形式表示[类型;长度]
,如下代码
如果数组的每个元素都相同,那么我们可以使用以下的声明方式
它就相当于以下表达式
3.2.4 数组的访问¶
数组是stack上分配的单个块内存,可以使用索引来访问数组元素,如下读取months
这个数组的第一个元素
rust不允许继续访问其数据相邻的地址的内存,运行的时候会报数组越界的错误(runtime时会panic)。如果访问的数组索引超出了数组的范围,编译的时候可能不会通过,因为rust回对编译的代码进行简单的检查,如下
fn main() {
let months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
let index = 15;
print!("{}", months[index]);
}
以上代码编译时将会报错,因为rust通过简单的判断能判定处months
元素个数只有12个,使用15
这个索引值访问是不被允许的。但如果我们把逻辑变得更加复杂,rust就没法在编译时进行检查了,只能在运行时进行代码检查,执行过程中逻辑出错rust将会报错并退出程序。如下代码
fn main() {
let months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
let index = [15, 16, 17];
print!("{}", months[index[0]]);
}
因为有了编译时简单逻辑检查,也使得我们的程序更加安全。