Rust基本数据类型

立山  金牌会员 | 2022-9-16 17:19:15 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 924|帖子 924|积分 2772

基本类型

Rust 每个值都有其确切的数据类型,总的来说可以分为两类:基本类型和复合类型。 基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:

  • 数值类型: 有符号整数 (i8, i16, i32, i64, isize)、 无符号整数 (u8, u16, u32, u64, usize) 、浮点数 (f32, f64)、以及有理数、复数
  • 字符串:字符串字面量和字符串切片 &str
  • 布尔类型: true和false
  • 字符类型: 表示单个 Unicode 字符,存储为 4 个字节
  • 单元类型: 即 () ,其唯一的值也是 ()
类型推导与标注

Rust 是一门静态类型语言,也就是编译器必须在编译期知道我们所有变量的类型,但这不意味着你需要为每个变量指定类型,因为 Rust 编译器可以根据变量的值和上下文中的使用方式来自动推导出变量的类型,但在某些情况下,它无法推导出变量类型,需要手动去给予一个类型标注
整数

整数是没有小数部分的数字。之前使用过的 i32 类型,表示有符号的 32 位整数( i 是英文单词 integer 的首字母,与之相反的是 u,代表无符号 unsigned 类型)。下表显示了 Rust 中的内置的整数类型:
长度有符号类型无符号类型8 位i8u816 位i16u1632 位i32u3264 位i64u64128 位i128u128视架构而定isizeusize类型定义的形式统一为:有无符号 + 类型大小(位数)。无符号数表示数字只能取正数,而有符号则表示数字既可以取正数又可以取负数。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以补码形式存储。
每个有符号类型规定的数字范围是 -(2n - 1) ~ 2n - 1 - 1,其中 n 是该定义形式的位长度。因此 i8 可存储数字范围是 -(27) ~ 27 - 1,即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ 2n - 1,所以 u8 能够存储的数字为 0 ~ 28 - 1,即 0 ~ 255。
此外,isize 和 usize 类型取决于程序运行的计算机 CPU 类型: 若 CPU 是 32 位的,则这两个类型是 32 位的,同理,若 CPU 是 64 位,那么它们则是 64 位。
整形字面量可以用下表的形式书写:
数字字面量示例十进制98_222十六进制0xff八进制0o77二进制0b1111_0000字节 (仅限于 u8)b'A'这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整型默认使用 i32,例如 let i = 1,那 i 就是 i32 类型,因此你可以首选它,同时该类型也往往是性能最好的isize 和 usize 的主要应用场景是用作集合的索引。
整型溢出

假设有一个 u8 ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生整型溢出。关于这一行为 Rust 有一些有趣的规则:当在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 panic(崩溃,Rust 使用这个术语来表明程序因错误而退出)。
在当使用 --release 参数进行 release 模式构建时,Rust 检测溢出。相反,当检测到整型溢出时,Rust 会按照补码循环溢出(two’s complement wrapping)的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 u8 的情况下,256 变成 0,257 变成 1,依此类推。程序不会 panic,但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。
要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:

  • 使用 wrapping_* 方法在所有模式下都按照补码循环溢出规则处理,例如 wrapping_add
  • 如果使用 checked_* 方法时发生溢出,则返回 None 值
  • 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
  • 使用 saturating_* 方法使值达到最小值或最大值
浮点数

按照存储大小,把浮点型划分为 f32 和 f64。其中 f64 是默认的浮点类型。

  • f32 又称为 单精度浮点型
  • f64 又称为 双精度浮点型,它是 Rust 默认的浮点类型.
Rust 中不能将 0.0 赋值给任意一个整型,也不能将 0 赋值给任意一个浮点型。
  1. fn main() {
  2.     let x = 2.0; // f64
  3.     let y: f32 = 3.0; // f32
  4. }
复制代码
_下划线

当数字很大的时候,Rust 可以用 **(_下划线) ** ,来让数字变得可读性更好。
PLAINTEXT
  1. let price12 =1_000_000;
  2. println!("price12 {}", price12); //输出 price12 1000000
  3. let price13 =1_000_000.666_123;
  4. println!("price13 {}", price13);//输出 price13 1000000.666123
复制代码
浮点数陷阱

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

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

  • 避免在浮点数上测试相等性
  • 当结果在数学上可能存在未定义时,需要格外的小心
  1. fn main() {
  2.     let abc: (f32, f32, f32) = (0.1, 0.2, 0.3);
  3.     let xyz: (f64, f64, f64) = (0.1, 0.2, 0.3);
  4.     println!("abc (f32)");
  5.     println!("   0.1 + 0.2: {:x}", (abc.0 + abc.1).to_bits());
  6.     println!("         0.3: {:x}", (abc.2).to_bits());
  7.     println!();
  8.     println!("xyz (f64)");
  9.     println!("   0.1 + 0.2: {:x}", (xyz.0 + xyz.1).to_bits());
  10.     println!("         0.3: {:x}", (xyz.2).to_bits());
  11.     println!();
  12.     assert!(abc.0 + abc.1 == abc.2);
  13.     assert!(xyz.0 + xyz.1 == xyz.2);
  14. }
复制代码
输出如下:
  1. abc (f32)
  2.    0.1 + 0.2: 3e99999a
  3.          0.3: 3e99999a
  4. xyz (f64)
  5.    0.1 + 0.2: 3fd3333333333334
  6.          0.3: 3fd3333333333333
  7. thread 'main' panicked at 'assertion failed: xyz.0 + xyz.1 == xyz.2',
  8. ➥ch2-add-floats.rs.rs:14:5
  9. note: run with `RUST_BACKTRACE=1` environment variable to display
  10. ➥a backtrace
复制代码
对 f32 类型做加法时,0.1 + 0.2 的结果是 3e99999a,0.3 也是 3e99999a,因此 f32下的 0.1 + 0.2 == 0.3通过测试,但是到了 f64类型时,结果就不一样了,因为 f64精度高很多,因此在小数点非常后面发生了一点微小的变化,0.1 + 0.2以 4结尾,但是 0.3以结尾,这个细微区别导致 f64下的测试失败了,并且抛出了异常。
NaN

对于数学上未定义的结果,例如对负数取平方根 -42.1.sqrt() ,会产生一个特殊的结果:Rust 的浮点数类型使用 NaN (not a number)来处理这些情况。
所有跟 NaN 交互的操作,都会返回一个 NaN,而且 NaN 不能用来比较,下面的代码会崩溃:
  1. fn main() {
  2.   let x = (-42.0_f32).sqrt();
  3.   assert_eq!(x, x);
  4. }
复制代码
出于防御性编程的考虑,可以使用 is_nan() 等方法,可以用来判断一个数值是否是 NaN :
  1. fn main() {
  2.     let x = (-42.0_f32).sqrt();
  3.     if x.is_nan() {
  4.         println!("未定义的数学行为")
  5.     }
  6. }
复制代码
数字运算
  1. // 加法
  2. let sum = 5 + 10;
  3. // 减法
  4. let difference = 95.5 - 4.3;
  5. // 乘法
  6. let product = 4 * 30;
  7. // 除法
  8. let quotient = 56.7 / 32.2;
  9. // 求余
  10. let remainder = 43 % 5;
复制代码
  1. fn main() {
  2.   // 编译器会进行自动推导,给予twenty i32的类型
  3.   let twenty = 20;
  4.   // 类型标注
  5.   let twenty_one: i32 = 21;
  6.   // 通过类型后缀的方式进行类型标注:22是i32类型
  7.   let twenty_two = 22i32;
  8.   // 只有同样类型,才能运算
  9.   let addition = twenty + twenty_one + twenty_two;
  10.   println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);
  11.   // 对于较长的数字,可以用_进行分割,提升可读性
  12.   let one_million: i64 = 1_000_000;
  13.   println!("{}", one_million.pow(2));
  14.   // 定义一个f32数组,其中42.0会自动被推导为f32类型
  15.   let forty_twos = [
  16.     42.0,
  17.     42f32,
  18.     42.0_f32,
  19.   ];
  20.   // 打印数组中第一个值,并控制小数位为2位
  21.   println!("{:.2}", forty_twos[0]);
  22. }
复制代码
位运算

Rust的运算基本上和其他语言一样
运算符说明& 位与相同位置均为1时则为1,否则为0位或^ 异或相同位置不相同则为1,相同则为0! 位非把位中的0和1相互取反,即0置为1,1置为0> 右移所有位向右移动指定位数,左位补零[code]fn main() {    // 二进制为00000010    let a:i32 = 2;    // 二进制为00000011    let b:i32 = 3;    println!("(a & b) value is {}", a & b);                // (a & b) value is 2    println!("(a | b) value is {}", a | b);                // (a | b) value is 3    println!("(a ^ b) value is {}", a ^ b);                // (a ^ b) value is 1    println!("(!b) value is {} ", !b);                // (!b) value is -4    println!("(a > b) value is {}", a >> b);                // (a >> b) value is 0    let mut a = a;    // 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)    a
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立山

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表