自学内容网 自学内容网

【Rust 学习笔记】Rust 基础数据类型介绍(一)

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接

博客内容主要围绕:
       5G/6G协议讲解
       高级C语言讲解
       Rust语言讲解



Rust 基础数据类型介绍(一)

       Rust中的数据类型如下所示,我会分多篇博客来介绍,下面先看一个总览:

类型简要说明
i8、i16、i32、i64、i128、u8、u16、u32、u64、u128给定位宽的有符号整数和无符号整数
isize、usize与机器字(32bit、64bit)一样大的有符号整数和无符号整数
f32、f64单精度IEEE浮点数和双精度IEEE浮点数
bool布尔值
charUnicode字符,32位宽(4字节)
()单元元组(空元组)
(char,u8,i32)元组(允许混合类型)
Box<Attend>指向堆中值的拥有型指针
&i32、&mut i32共享引用和可变引用,非拥有型指针,其生命周期不能超出引用目标
StringUTF-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中数值类型的名称都遵循一种统一的模式,也就是位数表面它的宽度,以前缀表明它的用法,如下图所示:

位宽无符号整数有符号整数浮点数
8u8i8
16u16i16
32u32i32f32
64u64i64f64
128u128i128
机器字usizeisize

1.1 整型

1.1.1 无符号整型

       Rust 的无符号整型会使用它们的完整范围来表示正值和 0,如下表所示:

类型范围
u80~28-1(0~255)
u160~216-1(0~65535)
u320~232-1(0~4 294 967 295)
u640~264-1(0~18 446 744 073 709 551 615)
u1280~2128-1(0~255)
usize0~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_tptrdiff_t,他们的精度与目标机器上地址空间的大小一致,即在32位架构上是32位长,在64位架构上是64位长。Rust要求数组索引是usize值,用来表示数组或向量大小或数据结构中元素数量的值通常也是usize类型

1.1.3 整型字面量

       Rust中的整型字面量可以带上一个后缀来指示它的类型,例如,65u8是 u8类型。如果整型字面量没有携带类型后缀,Rust会延迟确定其类型,如果有多个候选类型,且包含i32,则选择 i32,否则报告类型歧义错误。

       前缀 0x0o0b 分别表示十六进制字面量、八进制字面量和二进制字面量。为了易读性,可以在数字之间任意插入下划线,例如,4_294_967_295。下划线的具体位置无关紧要,因此也可以将十六进制数或二进制数按 4 位数字而非 3 位数字进行分组(如 0xffff_ffff),或分隔开数字的类型后缀(如 127_u8)。

字面量类型十进制数值
116i8i8116
0xcafeu32u3251966
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 双精度浮点类型。这些类型包括正无穷大和负无穷大、不同的正零值和负零值,以及非数值。如下表所示:

类型精度范围
f32IEEE单精度(至少6位小数)大约 -3.4x1038 ~ +3.4x1038
f64IEEE双精度(至少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_ 后面可以跟着的运算名称如下表所示:

运算名称后缀例子
加法add100_i8.checked_add(27) == Some(127)
减法sub10_u8.checked_sub(11) == None
乘法mul128_u8.saturating_mul(3) == 255
除法div64_u16.wrapping_div(8) == 8
求余rem(-32768_i16).wrapping_rem(-1) == 0
取负neg(-128_i8).checked_neg() == None
绝对值abs(-32768_i16).wrapping_abs() == -32768
求幂pow3_u8.checked_pow(4) == Some(81)
按位左移shl10_u32.wrapping_shl(34) == 40
按位右移shr40_u64.wrapping_shr(66) == 10


在这里插入图片描述


原文地址:https://blog.csdn.net/qq_31985307/article/details/144069742

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!