Skip to content

DataType

本文主要介绍rust中的数据类型。总的来说可以分为两类:基本类型和复合类型还有自定义类型。

基本类型

基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:

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

数值类型

数值分为以下两种情况:

整数类型

TIP

每个有符号类型规定的数字范围是 ~ ,其中n是该定义形式的位长度。因此i8可存储数字范围是 ~ ,即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ ,所以u8能够存储的数字为 0 ~ ,即 0 ~ 255。

整数是没有小数部分的数字。之前使用过的i32类型,表示有符号的 32位整数(i是英文单词integer的首字母,与之相反的是u,代表无符号unsigned类型)。下表显示了Rust中的内置的整数类型:

长度有符号名称无符号名称
8位i8u8
16位i16u16
32位i32u32
64位i64u64
128位i128u128
视架构而定isizeusize

类型定义的形式统一为:有无符号 + 类型大小(位数)。无符号数表示数字只能取正数,而有符号则表示数字既可以取正数又可以取负数。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以补码形式存储。

此外isizeusize类型取决于程序运行的计算机CPU类型: 若CPU32位的,则这两个类型是32位的,同理,若CPU64位,那么它们则是64位。

这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整型默认使用i32,例如let i = 1,那i就是i32类型,因此你可以首选它,同时该类型也往往是性能最好的。isizeusize的主要应用场景是用作集合的索引。

整型溢出

假设有一个u8,它可以存放从0255的值。那么当你将其修改为范围之外的值,比如256则会发生整型溢出。关于这一行为Rust有一些有趣的规则:当在debug模式编译时Rust会检查整型溢出,若存在这些问题,则使程序在编译时panic(崩溃,Rust 使用这个术语来表明程序因错误而退出)。

浮点类型

浮点类型数字 是带有小数点的数字,在Rust中浮点类型数字也有两种基本类型:f32f64,分别为 32 位和 64 位大小。默认浮点类型是f64,在现代的CPU中它的速度与f32几乎相同,但精度更高。

浮点数根据 IEEE-754 标准实现。f32 类型是单精度浮点型,f64 为双精度。

浮点数计算与其他语言都存在精度的问题,如0.1

你需要遵守以下准则:

  1. 避免在浮点数上测试相等性
  2. 当结果在数学上可能存在未定义时,需要格外的小心。

NaN 对于数学上未定义的结果,例如对负数取平方根-42.1.sqrt() ,会产生一个特殊的结果:Rust 的浮点数类型使用NaN(not a number)来处理这些情况。

所有跟NaN交互的操作,都会返回一个NaN,而且NaN不能用来比较

出于防御性编程的考虑,可以使用is_nan()等方法,可以用来判断一个数值是否是NaN

rust
fn main() {
  let x = (-42.0_f32).sqrt();
  if x.is_nan() {
      println!("未定义的数学行为")
  }
}
fn main() {
  let x = (-42.0_f32).sqrt();
  if x.is_nan() {
      println!("未定义的数学行为")
  }
}

数字运算

Rust支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算:

rust
fn main() {
  // 加法
  let sum = 5 + 10;

  // 减法
  let difference = 95.5 - 4.3;

  // 乘法
  let product = 4 * 30;

  // 除法
  let quotient = 56.7 / 32.2;

  // 求余
  let remainder = 43 % 5;
}
fn main() {
  // 加法
  let sum = 5 + 10;

  // 减法
  let difference = 95.5 - 4.3;

  // 乘法
  let product = 4 * 30;

  // 除法
  let quotient = 56.7 / 32.2;

  // 求余
  let remainder = 43 % 5;
}

位运算

Rust的运算基本上和其他语言一样

运算符说明
& 位与相同位置均为1时则为1,否则为0
| 位或相同位置只要有1时则为1,否则为0
^ 异或相同位置不相同则为1,相同则为0
! 位非把位中的0和1相互取反,即0置为1,1置为0
<< 左移所有位向左移动指定位数,右位补零
>> 右移所有位向右移动指定位数,左位补零
rust
fn main() {
    // 二进制为00000010
    let a:i32 = 2;
    // 二进制为00000011
    let b:i32 = 3;

    println!("(a & b) value is {}", a & b);

    println!("(a | b) value is {}", a | b);

    println!("(a ^ b) value is {}", a ^ b);

    println!("(!b) value is {} ", !b);

    println!("(a << b) value is {}", a << b);

    println!("(a >> b) value is {}", a >> b);

    let mut a = a;
    // 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)
    a <<= b;
    println!("(a << b) value is {}", a);
}
fn main() {
    // 二进制为00000010
    let a:i32 = 2;
    // 二进制为00000011
    let b:i32 = 3;

    println!("(a & b) value is {}", a & b);

    println!("(a | b) value is {}", a | b);

    println!("(a ^ b) value is {}", a ^ b);

    println!("(!b) value is {} ", !b);

    println!("(a << b) value is {}", a << b);

    println!("(a >> b) value is {}", a >> b);

    let mut a = a;
    // 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)
    a <<= b;
    println!("(a << b) value is {}", a);
}

序列(Range)

Rust提供了一个非常简洁的方式,用来生成连续的数值,例如1..5,生成从14的连续数字,不包含51..=5,生成从15的连续数字,包含5,它的用途很简单,常常用于循环中:

rust
for i in 1..=5 {
  println!("{}",i);
}
for i in 1..=5 {
  println!("{}",i);
}

有理数和复数

Rust的标准库相比其它语言,准入门槛较高,因此有理数和复数并未包含在标准库中: 采用社区开发的数值库:num

  1. Cargo.toml中的[dependencies]下添加一行num = "0.4.0"
  2. src/main.rs文件中的main函数替换为下面的代码
  3. 运行`cargo run``
rust
use num::complex::Complex;

 fn main() {
   let a = Complex { re: 2.1, im: -1.2 };
   let b = Complex::new(11.1, 22.2);
   let result = a + b;

   println!("{} + {}i", result.re, result.im)
 }
use num::complex::Complex;

 fn main() {
   let a = Complex { re: 2.1, im: -1.2 };
   let b = Complex::new(11.1, 22.2);
   let result = a + b;

   println!("{} + {}i", result.re, result.im)
 }

字符(注意 不是字符串)

Rust的字符不仅仅是ASCII,所有的Unicode值都可以作为Rust字符,包括单个的中文、日文、韩文、emoji 表情符号等等,都是合法的字符类型。Unicode值的范围从U+0000 ~ U+D7FF 和 U+E000 ~ U+10FFFF。(字符类型占用4个字节与Unicode相等)

rust
fn main() {
    let x = '中';
    println!("字符'中'占用了{}字节的内存大小",std::mem::size_of_val(&x));
}
fn main() {
    let x = '中';
    println!("字符'中'占用了{}字节的内存大小",std::mem::size_of_val(&x));
}

布尔(bool)

Rust 中的布尔类型有两个可能的值:true 和 false,布尔值占用内存的大小为 1 个字节:

rust
fn main() {
    let t = true;

    let f: bool = false; // 使用类型标注,显式指定f的类型

    if f {
        println!("这是段毫无意义的代码");
    }
}
fn main() {
    let t = true;

    let f: bool = false; // 使用类型标注,显式指定f的类型

    if f {
        println!("这是段毫无意义的代码");
    }
}

单元类型

单元类型就是(),对,你没看错,就是(),唯一的值也是() 比如:

  1. 函数main().
  2. 你可以用()作为map的值,表示我们不关注具体的值,只关注key。这种用法和Go语言的struct{}类似,可以作为一个值用来占位,但是完全不占用任何内存。

总结

  1. Rust类型转换必须是显式的(js默认是隐式的).
  2. Rust的数值上可以使用方法。例如你可以用以下方法来将13.14取整:13.14_f32.round(),在这里我们使用了类型后缀,因为编译器需要知道13.14的具体类型.

数据类型

Released under the MIT License.