参考:https://kaisery.github.io/trpl-zh-cn/ch03-02-data-types.html、https://doc.rust-lang.org/book/ch03-02-data-types.html
除了原生数据类型之外,还可以自定义数据类型:struct
、enum
;以及可以定义常量:const
、static
。
这里的原生数据类型都是存储在栈上的,并且当离开作用域时被移出栈。
标量 (scalar)
整型 (int)
- 从类型和范围看:
| 长度 | 有符号 | 范围 | 无符号 | 范围 |
| —- | —- | —- | —- | —- |
| 8-bit |
i8
| -128 ~ 127 |u8
| 0 ~ 255 | | 16-bit |i16
| -32,768 ~ 32,767 (约正负 3 万,3E4) |u16
| 0 ~ 65,536 (约 6 万,6E4) | | 32-bit |i32
| -2,147,483,648 ~ 2,147,483,647
(约正负 21 亿,2E9) |u32
| 0 ~ 4,294,967,296 (约 42 亿,4E9) | | 64-bit |i64
| ±9.2E18 |u64
| 0 ~ 1.8E19 | | 128-bit |i128
| ±1.7E38 |u128
| 0 ~ 3.4E38 | | arch |isize
| 依赖运行程序的计算机架构:64 或 32 位 |usize
| 64 或 32 位 |
每一个有符号的变体可以储存包含从 到 $ 2^{n - 1} - 1$ 在内的数字,这里 n 是变体使用的位数。所以 i8
可以储存从 -(2^7 ) 到 2^7 - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从 0 到 的数字,所以 u8
可以储存从 0 到 2^8 - 1 的数字,也就是从 0 到 255。
- 从输入的形式看:
| 数字字面值 | 例子 |
| —- | —- |
| Decimal (十进制) |
98_222
| | Hex (十六进制) |0xff
| | Octal (八进制) |0o77
| | Binary (二进制) |0b1111_0000
| | Byte (单字节字符)(仅限于u8
) |b'A'
|
除 byte 以外的所有数字字面值允许使用类型后缀,例如 57u8
、57f64
;同时也允许使用 _
做为分隔符以方便读数,例如1_000
。
- Rust 的默认整型是
**i32**
:它通常是最快的,甚至在 64 位系统上也是。isize
或usize
主要作为某些集合的索引。 - 整型溢出:Rust Book: data-types | integer-overflow
在 debug 模式编译时,Rust 检查这类问题并使程序 panic (程序因错误而退出)。
在 release 构建中,Rust 不检测溢出,相反会进行一种被称为二进制补码包装(two’s complement wrapping )的操作:256
变成 0
,257
变成 1
,依此类推。
依赖整型溢出被认为是一种错误,即便可能出现这种行为。如果你确实需要这种行为,标准库中有一个类型显式提供此功能,Wrapping
。
浮点型 (float)
f32
和 f64
,分别占 32 位和 64 位。
默认类型是 **f64**
,因为在现代 CPU 中,它与 f32
速度几乎一样,不过精度更高。
注意:Rust 中的浮点型必须带小数点 .
,比如 1
是默认 i32
,而 1.
默认才是 f64
。在基础的加减乘除中,运算符号前后必须是同一类型,如 1+1.
是不允许的。
布尔型 (bool)
字符型 (char)
Rust 的 char
类型由 **'**
(单引号)指定,大小为四个字节 (4 bytes),并代表了一个 Unicode 标量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。
拼音字母(Accented letters)、单个数字 0-9、中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 char
值。
Unicode 标量值包含从U+0000
到U+D7FF
和U+E000
到U+10FFFF
在内的值。
字符串字面值(str)
Rust 的 str
类型由 **"**
(双引号)指定,全称为 string literals,是被硬编码进程序里的字符串值,编译后被储存在二进制文件中,是不可(在程序运行时)变的。
硬编码 是指将可变变量用一个固定值来代替的方法,将数据直接嵌入到程序或其他可执行对象的源代码中,不同于从外部获取数据或在运行时生成数据。 硬编码数据通常只能通过编辑源代码和重新编译可执行文件来修改,尽管可以使用调试器或十六进制编辑器在内存或磁盘上进行更改。
在编译时就知道其内容,所以文本被直接硬编码进最终的可执行文件中。这使得字符串字面值快速且高效。
Rust 的核心语言中只有一种字符串类型:str
,即字符串 slice,它通常以被借用的形式出现,&str
。而字符串 slice 是一些储存在别处的 UTF-8 编码字符串数据的引用。比如字符串字面值被储存在程序的二进制输出中,字符串 slice 也是如此。
另一种可变的 类型:”String”
复合 (compound)
元组 (tuple)
元组是将多个其他类型的值用 (
)
和 ,
组合起来。元组在栈上不一定是连续存储的,也无法遍历 (traverse or iterate) 元组。
元组长度是固定的:一旦声明,其长度不会增大或缩小。
定义与索引:
let tup: (i32, f64, u8) = (500, 6.4, 1); // 定义元组数据
let tup = (500, 6.4, 1); // 类型注解不是必要的
let (x, y, z) = tup; // 使用模式匹配(pattern matching)来解构(destructure)元组值
// 或者使用点号 `.` 后跟值的索引来直接访问,元组的第一个索引值是 0;如果超出索引会 panic
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
元组的一些特殊情况:
()
是 unit 类型,属于标量类型,通常作为语句(比如表达式;
)的值,或者函数未标注返回值类型时默认的返回值(single_element,)
和(literal)
是不同的类型,前者表示 单元素元组,后者表示 括号中的字面值(类比在表达式外层加括号其实就是指表达式本身:(1+1)
和1+1
是一样的)// 创建单元素元组需要一个额外的逗号,这是为了和被括号包含的字面量作区分。
println!("one element tuple: {:?}", (5u32,));
println!("just an integer: {:?}", (5u32)); // 加括号的原因是这个值的描述比较多,把括号里面的内容作为整体,而不是作为元组的元素
数组 (array)
数组是将相同类型的值用[]
组合起来。
数组的长度是固定的:一旦声明,其长度不会增大或缩小。
数组每个元素的类型必须相同。
数组是一整块分配在栈上的连续存储的内存。可以遍历 (traverse or iterate) 数组。
定义与索引:
越界访问会let a: [i32; 5] = [1, 2, 3, 4, 5]; // 定义数组,与元组类似
let a = [1, 2, 3, 4, 5]; // 类型注解不是必要的
let a = [3; 5]; // 由于是数组元素是单一的类型,所以可以使用 `[初始值;个数]` 来生成一组元素相同的数组
let a = [3, 3, 3, 3, 3]; // 这个与上面一条等价
let a: [i32; 5] = [3; 5]; // `[初始值;个数]` 的形式也可用在类型注解上
panic
。切片 (slice)
“slice”
打印结果:// 数组与 slice 的例子
use std::mem;
// 此函数借用一个 slice
fn analyze_slice(slice: &[i32]) {
println!("first element of the slice: {}", slice[0]);
println!("the slice has {} elements", slice.len());
}
fn main() {
// 定长数组(类型标记是多余的)
let xs: [i32; 5] = [1, 2, 3, 4, 5];
// 所有元素可以初始化成相同的值
let ys: [i32; 500] = [0; 500];
// 下标从 0 开始
println!("first element of the array: {}", xs[0]);
println!("second element of the array: {}", xs[1]);
// `len` 返回数组的大小
println!("array size: {}", xs.len());
// 数组是在栈中分配的
println!("array occupies {} bytes", mem::size_of_val(&xs));
// 数组可以自动被借用成为 slice
println!("borrow the whole array as a slice");
analyze_slice(&xs);
// slice 可以指向数组的一部分
println!("borrow a section of the array as a slice");
analyze_slice(&ys[1 .. 4]);
// 越界的下标会引发致命错误(panic)
// println!("{}", xs[5]);
}
first element of the array: 1
second element of the array: 2
array size: 5
array occupies 20 bytes
borrow the whole array as a slice
first element of the slice: 1
the slice has 5 elements
borrow a section of the array as a slice
first element of the slice: 0
the slice has 3 elements