当前位置: 首页 > 工具软件 > C2Rust > 使用案例 >

rust基本数据类型——标量类型

廖臻
2023-12-01

基本数据类型(标量类型)

在rust里数据类型可以分为标量(scalar)和复合(compound)类型,标量类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。rust是静态强类型语言,它在编译时就需要知道所有变量的类型,并且不同类型的数据之间是不允许进行运算的。

整数类型

rust根据整数占据的二进制位数,提供了以下多种整数类型。可以根据业务的实际场景进行选择。当你没有指定整数的类型时,rust 整型默认使用 i32类型。无符号数表示数字只能取非负数,而有符号则表示数字既可以取非负数,也可以取负数。

类型长度有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

需要注意的是arch,它表示长度取决于CPU的架构。isize 和 usize 类型取决于程序运行的计算机 CPU 类型: 若 CPU 是 32 位的,则这两个类型是 32 位的,同理,若 CPU 是 64 位,那么它们则是 64 位。isize 和 usize 的主要应用场景是用作集合数据类型的索引。

一个有符号的变量可以储存包含从 -2^(n-1) 到 2^(n-1)-1 之间的数值,n是类型的长度;而无符号数存储的范围为 0 到 2^(n-1)之间。

整数溢出

当在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 panic(崩溃,Rust 使用这个术语来表明程序因错误而退出)。在当使用 --release 参数进行 release 模式构建时,Rust 不检测溢出。 当整型溢出时,Rust 会按照补码循环溢出(two’s complement wrapping)的规则处理。要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:

  • 使用 wrapping_* 方法在所有模式下都按照补码循环溢出规则处理,例如 wrapping_add
  • 如果使用 checked_* 方法时发生溢出,则返回 None 值
  • 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
  • 使用 saturating_* 方法使值达到最小值或最大值
    下面是一个示例,演示了这四种方式分别处理的结果。关于其中涉及的其它数据类型,后续才会接触到。
fn main() {
    let a: i8 = 127;
    let b: i8 = a.wrapping_add(1);      // 所有模式下都按照补码循环溢出规则
    println!("{b}");

    let b:Option<i8> = a.checked_add(0);
    if b.is_none(){             // 发生溢出时,返回None值。
        println!("溢出");
    }else{
        println!("{:?}", b.expect(""));
    }

    let tup = a.overflowing_add(1);     //返回一个数值以及指示是否溢出的bool值。
    println!("{}, {}", tup.0, tup.1);               // tup是一个元组,取其中的第0个和第1个元素的值。

    let b = a.saturating_add(1);                //使值达到最大值(或者最小值)       
    println!("{b}");
}

浮点类型

浮点类型数字 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: f32 和 f64,分别为 32 位和 64 位大小。默认浮点类型是 f64。浮点数采用 IEEE-754 标准表示。f32 是单精度浮点数,f64 是双精度浮点数。

NaN

对于数学上未定义的结果,Rust使用NaN来处理这些结果。所有跟 NaN 交互的操作,都会返回一个 NaN,而且两个 NaN 之间是不能用来比较的。一个防御性编程的例子如下所示:

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

数值运算

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

fn main() {
    // 加法
    let sum = 5 + 10;
    println!("{}", sum);
    // 减法
    let difference = 95.5 - 4.3;    
    println!("{}", difference);

    // 乘法
    let product = 4 * 30;
    println!("{}", product);

    // 除法
    let quotient = 56.7 / 32.2;
    println!("{}", quotient);

    // 求余
    let remainder = 43 % 5;
    println!("{}", remainder);

}

再次强调,Rust是一门强类型语言,不同类型之间不允许进行运算。例如可以在上述代码中混合计算整型和浮点型,将会导致错误。

fn main() {
    let sum: f64 = 12i32 + 3.3_f64;    // 错误,rust无法将i32和f64进行相加操作。
    println!("{sum}")
}

数字类型的数字字面值允许使用类型后缀,例如上面的12i32来指定12这个数字是i32类型,同时也允许使用 _ 做为分隔符以方便人类读,例如:3.3_f64。同时_也可以和python中类似,用来对大数进行多位分割书写。例如:

let x: i64 = 100_0000_0000;    

为了让i32和f64直接可以进行运算,Rust要求必须对其中一个类型进行显示类型转换,让它们变成同一个类型。在Rust中可以使用as运算符进行数据类型转换。例如:

fn main() {
    // let sum: f64 = 12i32+ 3.3_f64;   // 错误
    let mut sum: f64 = 12i32 as f64+ 3.3_f64;   // 进行显示类型转换
    println!("{sum}");
    sum = (12_i32 + 3.3_f64 as i32).into();     // 先将3.3从f64转为i32,那么3.3就会变成3,然后在将12+3的结果转为f64
    println!("{sum}");
    sum = (12_i32 + 3.3_f64 as i32) as f64;     // 等价于into()
    println!("{sum}");
}

另外一个需要注意的例子如下所示:

fn main() {
    println!("{}", 1u8 + 21);
}

这个例子是可以正常运行的,它背后的原理是rust编译器会推断21这个数字的类型为u8,这样就相当于是1u8 + 21u8,所以可以正常运行。

布尔型

布尔类型表示真假。在Rust中,使用关键字bool表示,其有两个值true和false。布尔值占用内存的大小为 1 个字节。布尔类型的场景主要在于流程控制。

字符类型

在Rust中,使用char表示字符类型,和C/C++语言中类似,不同的是Rust的char类型大小为4个字节,并且是一个Unicode字符。注意,前面我们在代码中使用双引号(“”)来声明的不是字符类型,而是字符串类型;字符类型使用单引号(‘’)声明。下面是一个例子。

fn main() {
    let c1:char = '☺';
    let c2:char = '秀';
    let c3:char = 'a';

    println!("{c1}");
    println!("{c2}");
    println!("{c3}");
}

共同点

下面是数据类型之间的共同点总结

  1. 不允许不同的数据类型之间进行直接运算;
  2. 变量在声明的时候如果没有进行初始化,Rust是不会赋予其默认值的;
  3. 未经初始化的变量不允许被使用。

参考资料

rust圣经
rust程序设计语言

 类似资料: