博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接
博客内容主要围绕:
5G/6G协议讲解
高级C语言讲解
Rust语言讲解
文章目录
- Rust 基础数据类型介绍(一)
- 一、固定宽度的数值类型
- 1.1 整型
- 1.1.1 无符号整型
- 1.1.2 有符号整型
- 1.1.3 整型字面量
- 1.1.4 字节字面量
- 1.2 浮点类型
- 1.3 转换运算符
- 1.4 检查算法、回绕算法、饱和算法和溢出算法
- 1.4.1 检查运算
- 1.4.2 回绕运算
- 1.4.3 饱和运算
- 1.4.4 溢出运算
Rust 基础数据类型介绍(一)
Rust中的数据类型如下所示,我会分多篇博客来介绍,下面先看一个总览:
类型 | 简要说明 |
---|---|
i8、i16、i32、i64、i128、u8、u16、u32、u64、u128 | 给定位宽的有符号整数和无符号整数 |
isize、usize | 与机器字(32bit、64bit)一样大的有符号整数和无符号整数 |
f32、f64 | 单精度IEEE浮点数和双精度IEEE浮点数 |
bool | 布尔值 |
char | Unicode字符,32位宽(4字节) |
() | 单元元组(空元组) |
(char,u8,i32) | 元组(允许混合类型) |
Box<Attend> | 指向堆中值的拥有型指针 |
&i32、&mut i32 | 共享引用和可变引用,非拥有型指针,其生命周期不能超出引用目标 |
String | UTF-8字符串,动态分配大小 |
&str | 对str的引用,指向UTF-8文本的非拥有型指针 |
[f64;4]、[u8;256] | 数组,固定长度,其元素类型都相同 |
Vec[f64] | 向量,可变长度,其元素类型都相同 |
&[u8]、*mut [u8] | 对切片(数组或向量某一部分)的引用,包含指针和长度 |
Option<&str> | 可选值,或为None(无值),或者为Some(v)(有值,其值为v) |
Result<u64, Error> | 可能失败的操作结果,或者为成功值OK(v),或者为错误值Err(e) |
struct S { x: f32, y: f32 } | 具名字段型结构体 |
struct T(i32, char); | 元组型结构体 |
struct E; | 单元型结构体,无字段 |
enum Attend { OnTime, Late(u32)} | 枚举,或代数数据类型 |
&dyn Any、&mut dyn Read | 特型(trait)对象,是对任何实现了一组给定方法的值的引用 |
fn(&str)->bool | 函数指针 |
(闭包类型没有显式书写形式) | 闭包 |
一、固定宽度的数值类型
Rust中数值类型的名称都遵循一种统一的模式,也就是位数
表面它的宽度,以前缀
表明它的用法,如下图所示:
位宽 | 无符号整数 | 有符号整数 | 浮点数 |
---|---|---|---|
8 | u8 | i8 | |
16 | u16 | i16 | |
32 | u32 | i32 | f32 |
64 | u64 | i64 | f64 |
128 | u128 | i128 | |
机器字 | usize | isize |
1.1 整型
1.1.1 无符号整型
Rust 的无符号整型会使用它们的完整范围来表示正值和 0,如下表所示:
类型 | 范围 |
---|---|
u8 | 0~28-1(0~255) |
u16 | 0~216-1(0~65535) |
u32 | 0~232-1(0~4 294 967 295) |
u64 | 0~264-1(0~18 446 744 073 709 551 615) |
u128 | 0~2128-1(0~255) |
usize | 0~232-1 或 0~264-1 |
1.1.2 有符号整型
Rust 的有符号整型会使用二进制补码表示,使用与相应的无符号类型相同的位模式来覆盖正值和负值的范围,如下表所示:
类型 | 范围 |
---|---|
i8 | -27 ~ 27-1(-128 ~ 127) |
i16 | -215 ~ 215-1(-32768 ~ 32767) |
i32 | -231 ~ 231-1(-2 147 483 648 ~ 2 147 483 647) |
i64 | -263 ~ 263-1 |
i128 | -2127 ~ 21278-1(0~255) |
isize | -231 ~ 231-1 或 -263 ~ 263-1 |
Rust会将u8
类型作为字节值。例如,从二进制文件或套接字中读取数据时会产生一个 u8 值构成的流。usize
类型和isize
类型类似于C和C++中的size_t
和ptrdiff_t
,他们的精度与目标机器上地址空间的大小一致,即在32位架构上是32位长,在64位架构上是64位长。Rust要求数组索引是usize值,用来表示数组或向量大小或数据结构中元素数量的值通常也是usize类型
。
1.1.3 整型字面量
Rust中的整型字面量
可以带上一个后缀来指示它的类型,例如,65u8是 u8类型。如果整型字面量没有携带类型后缀,Rust会延迟确定
其类型,如果有多个候选类型,且包含i32,则选择 i32,否则报告类型歧义错误。
前缀 0x
、0o
和 0b
分别表示十六进制字面量、八进制字面量和二进制字面量。为了易读性,可以在数字之间任意插入下划线
,例如,4_294_967_295。下划线的具体位置无关紧要
,因此也可以将十六进制数或二进制数按 4 位数字而非 3 位数字进行分组(如 0xffff_ffff),或分隔开数字的类型后缀(如 127_u8)。
字面量 | 类型 | 十进制数值 |
---|---|---|
116i8 | i8 | 116 |
0xcafeu32 | u32 | 51966 |
0b0010_1010 | 推断 | 42 |
0o106 | 推断 | 70 |
下面是一个很有趣的问题,当我们运行
println!("{}", (-4).abs());
Rust编译器会报错,提示无法确定数据类型:
通常情况下是不需要指明数据类型的,Rust会自行推断,但是上面的例子中没有什么方法可用于推测 -4 的类型,因此我们需要显示的告诉Rust,例如下面的代码:println!("{}", (-4_i32).abs()); println!("{}", i32::abs(-4));
另外需要注意,因为
.
运算符的优先级高于一元前缀运算符
,所以需要在 -4 周围添加括号!
1.1.4 字节字面量
Rust为 u8 类型提供了字节字面量
,例如,b’x’ 表示以字符 x 的ASCII码作为 u8的值。需要注意的是只有ASCII字符才能出现在字节字面量中
。下面几个字符是需要使用转义字符来表示的:
字符 | 字节字面量 | 等效数值 |
---|---|---|
单引号(') | b’‘’ | 39u8 |
反斜杠(\) | b’\’ | 92u8 |
换行(lf) | b’\n’ | 10u8 |
回车(cr) | b’\r’ | 13u8 |
制表符(tab) | b’\t’ | 9u8 |
1.2 浮点类型
Rust 提供了 IEEE 单精度浮点类型和 IEEE 双精度浮点类型。这些类型包括正无穷大和负无穷大、不同的正零值和负零值,以及非数值。如下表所示:
类型 | 精度 | 范围 |
---|---|---|
f32 | IEEE单精度(至少6位小数) | 大约 -3.4x1038 ~ +3.4x1038 |
f64 | IEEE双精度(至少15位小数) | 大约 -1.8x10308 ~ +1.8x10308 |
Rust 的 f32 和 f64 分别对应于 C 和 C++(在支持 IEEE 浮点的实现中)以及 Java(始终使用 IEEE 浮点)中的 float 类型和 double 类型。
如果浮点字面量缺少类型后缀,那么Rust会检查上下文以查看值的使用方式,如果发现两种浮点类型都合适,就会默认选择f64
。
1.3 转换运算符
可以使用as
运算符将一种整型类型转换为另一种整型类型。例如,
assert_eq!( 10_i8 as u16, 10_u16); // 范围内转换
assert_eq!( 2525_u16 as i16, 2525_i16); // 范围内转换assert_eq!( -1_i16 as i32, -1_i32); // 带符号扩展
assert_eq!(65535_u16 as i32, 65535_i32); // 填零扩展// 超出目标范围的转换生成的值等于原始值对2N取模的值,
// 其中N是按位算的目标宽度。有时这也称为“截断”
assert_eq!( 1000_i16 as u8, 232_u8);
assert_eq!(65535_u32 as i16, -1_i16);assert_eq!( -1_i8 as u8, 255_u8);
assert_eq!( 255_u8 as i8, -1_i8);
1.4 检查算法、回绕算法、饱和算法和溢出算法
当整型算术运算溢出时,Rust在调试
构建中会出现 panic。而在发布
构建中,运算会回绕,即它生成的值等于“数学意义上正确的结果”对“值类型范围”取模的值(在任何情况下不会出现像C和C++中那样出现“溢出未定义”的行为)。
如果这种默认行为不是你想要的,则整型提供的某些方法可以让你准确地阐明自己期望的行为。
1.4.1 检查运算
检查运算会返回结果的Option 值
:如果数学意义上正确的结果可以表示为该类型的值,那么就为 Some(v),否则为 None。
// 10与20之和可以表示为u8
assert_eq!(10_u8.checked_add(20), Some(30));// 很遗憾,100与200之和不能表示为u8
assert_eq!(100_u8.checked_add(200), None);// 做加法。如果溢出,则会出现panic
let sum = x.checked_add(y).unwrap();// 奇怪的是,在某种特殊情况下,带符号的除法也会溢出。
// 带符号的n位类型可以表示-2^(n-1),但不足以表示2^(n-1)
assert_eq!((-128_i8).checked_div(-1), None);
1.4.2 回绕运算
回绕运算会返回与“数学意义上正确的结果”对“值类型范围”取模的值相等的值。
// 第一个结果可以表示为u16,第二个则不能,所以会得到250000 对216的模
assert_eq!(100_u16.wrapping_mul(200), 20000);
assert_eq!(500_u16.wrapping_mul(500), 53392);// 对有符号类型的运算可能会回绕为负值
assert_eq!(500_i16.wrapping_mul(500), -12144);// 在移位运算中,移位距离会在值的大小范围内回绕,
// 所以在16位类型中移动17位就相当于移动了1位
assert_eq!(5_i16.wrapping_shl(17), 10);
1.4.3 饱和运算
饱和运算会返回最接近“数学意义上正确结果”的可表达值。换句话说,结果“紧贴着”该类型可表达的最大值和最小值。
assert_eq!(32760_i16.saturating_add(10), 32767);
assert_eq!((-32760_i16).saturating_sub(10), -32768);
不存在饱和除法、饱和求余法或饱和位移法
1.整数相除一般不会溢出,即使溢出也没有“数学意义上的正确结果”
2.饱和是对溢出的一种补救方式,余数不可能溢出,因此饱和也没有意义
3.饱和是对溢出的一种补救方式,移位的溢出在不同情况下补救方式不同,因此无法统一支持
1.4.4 溢出运算
溢出运算会返回一个元组 (result, overflowed)
,其中 result 是函数的回绕版本所返回的内容,而 overflowed 是一个布尔值,指示是否发生过溢出。
assert_eq!(255_u8.overflowing_sub(2), (253, false));
assert_eq!(255_u8.overflowing_add(2), (1, true));
前缀 checked_、wrapping_、saturating_ 或 overflowing_ 后面可以跟着的运算名称如下表所示:
运算 | 名称后缀 | 例子 |
---|---|---|
加法 | add | 100_i8.checked_add(27) == Some(127) |
减法 | sub | 10_u8.checked_sub(11) == None |
乘法 | mul | 128_u8.saturating_mul(3) == 255 |
除法 | div | 64_u16.wrapping_div(8) == 8 |
求余 | rem | (-32768_i16).wrapping_rem(-1) == 0 |
取负 | neg | (-128_i8).checked_neg() == None |
绝对值 | abs | (-32768_i16).wrapping_abs() == -32768 |
求幂 | pow | 3_u8.checked_pow(4) == Some(81) |
按位左移 | shl | 10_u32.wrapping_shl(34) == 40 |
按位右移 | shr | 40_u64.wrapping_shr(66) == 10 |