Rust学习1-数据类型

Rust学习1-数据类型

MidCHeck 938 2022-04-30

Rust有两种数据类型:标量值(scalar)和复合值(compound)。

本篇环境为Ubuntu22.04,x86_64,Rust版本为1.60.0。

标量值

标量类型标识一个单独的值,Rust有4种主要的标量类型:整型、浮点型、布尔类型、字符类型。

这四种类型与Perl中的Scalar Literals概念类似,都是字面值,所见即所得。

Rust默认数据类型为4字节的i32,与C/C++保持一致。

整型

与C/C++中定义相同,分为有符号与无符号两种,支持加、减、乘、除、模运算。

整数除法与C/C++一致,向下取余,例如2/3 == 0。

字节数 比特数 Rust C/C++
1 8 i8/u8 char/unsigned char
2 16 i16/u16 short/unsigned short
4 32 i32/u32 int/unsigned int
8 64 i64/u64 long long/unsigned long long
4/8 32/64 isize/usize long/unsigned long

isize和usize属于自适应类型,根据架构来决定大小,在32位架构就是4字节,64位架构为8字节,对应gcc编译器下的long和unsigned long类型,微软的msvc编译器没有这一特性,需要用宏来判断。自适应类型非常好用,尤其是在C/C++中用内存地址与指针进行互转时。

或许MSVC不支持自适应类型的原因之一就是Windows只能在有限的架构上运行?(笑)

整型表示

为了便于阅读,不用怼着屏幕数数字个数,Rust可以使用’_'字符作为数字的分隔符。

字面值 例子
十进制 98_222
十六进制 0xff
八进制 0o77
二进制 0b1111_0000
字节 b’A’

字节类型为单字节字符,仅限于u8,与Python中的Bytes完全不同。

整型溢出问题

在debug模式编译下,Rust会检查并使程序panic,而release模式下,Rust不检测溢出,会执行2的补码包装(two’s complement wrapping)操作,例如在u8类型下,值256会变成0,257变成1,即

value = value % 2^(sizeof(value)*8)

整型溢出在Rust中被认为是一种错误,但如果编程时确实需要使用溢出,可以在数字类型上使用标准库中的Wrapping功能:

  • 包装所有模式:wrapping_*,例如wrapping_add
  • 返回None值:checked_*
  • 返回布尔值:overflowing_*,指示是否发生溢出
  • 饱和处理:saturating_*,在边界上饱和处理成最大或最小值

饱和处理,对数字值进行计算时,发生上溢时返回最大值,下溢时返回最小值,无符号最小值为0,有符号时值为0 - (2^n-1)。

/* 函数签名 */
pub trait Saturating { 
    fn saturating_add (self, v: Self) -> Self;	// 返回a+b,溢出时返回该类型的最大值
    fn saturating_sub (self, v: Self) -> Self;  // 返回a-b,溢出时返回该类型的最小值
}

浮点型

Rust浮点类型使用IEEE-754标准表示,有两种浮点类型,f32和f64,分别占4字节和8字节,对应C/C++中的float和double,所有的浮点类型都是有符号的。

Rust认为在现代CPU上f32与f64速度相当,但精度更高,所以默认类型是f64,与Python相同(如果是在32位芯片上,个人还是建议使用f32)。

let x = 2.0;		// f64
let y: f32 = 3.0;	// f32

布尔型

类型名称为bool,分为true和false,常用于条件表达式中。

字符型

Rust的字符型数据使用4个字节存储,表示为char,与C/C++中一样使用单引号,字符串字面值使用双引号。

字符型表示一个Unicode代码点,范围为U+0000到U+D7FF,U+E000到U+10FFFF。

有点好奇Rust中正则的用法了。

复合类型

Rust有两个原生复合类型:元组(tuple)和数组(array)。

元组类型

元组使用圆括号,是长度固定的数组(C系语言概念),可以包含一个或多个类型的值,第一个元素的索引值为0。

// 示例函数
fn main() {
  // 使用类型注解
  let tup: (i32, f64, u8) = (500, 6.4, 1); 
  // 可以省略类型注解
  let tup = (500, 6.4, 1);	
  
  // 使用模式匹配(pattern matching)对元组进行解构(destructure)
  let (x, y, z) = tup;		
  
  // 使用点号'.'跟索引值来直接访问
  let five_hundred = x.0;
  let six_point_four = x.1;
  let one = x.2;
}

没有任何元素的元组是一种特殊类型,表示为( ),只有一个值,和表示相同,写成( ),被称为单位类型(unit type),值被称为单位值(unit value)。如果表达式没有显示地返回值,则隐式地返回该单位值。

数组类型

数组使用方括号,Rust中数组长度是固定的,并且每个值的类型都相同。

let a = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5];   // 带注解的数组类型写法

let a = [3, 3, 3, 3, 3,];	// 初始值为5个3
let a = [3; 5];			// 初始值为5个3,与上一行效果相同

// 确定了元素个数不会改变时,建议使用数组
let months = ["January", "February", "March", "April", "May", "June", "July",
              "August", "September", "October", "November", "December"];

// 数组是在栈上分配已知固定大小的内存块
// 可以使用索引访问
let first = a[0];
left second = a[1];

数组的栈溢出

当数组在运行时发生索引溢出时,程序会退出并输出错误信息。Rust在使用数组时,会在编译时和运行时都检查索引是否小于数组长度,当编译时发现溢出,编译器会报错,运行发生溢出时,程序会panic。

此原则会尽量避免像C/Python/Java等语言中忘记检查输入范围而导致栈溢出漏洞情况的出现。

编译时检查:
// 示例代码main.rs
fn main() {
    let a = [1, 2, 3, 4, 5];
    
    let five = a[5];

    println!(
        "The six value of the element at index {}",
        five
    );
}

进行编译:

xxx@ubuntu:~/Rust/array$ cargo build
   Compiling array v0.1.0 (/home/xxx/Rust/array)
error: this operation will panic at runtime
 --> src/main.rs:5:16
  |
5 |     let five = a[5];
  |                ^^^^ index out of bounds: the length is 5 but the index is 5
  |
  = note: `#[deny(unconditional_panic)]` on by default

error: could not compile `array` due to previous error
运行时检查:
use std::io;

fn main() {
    let a = [1, 2, 3, 4, 5];

    println!("Please enter an array index.");

    let mut index = String::new();

    io::stdin()
        .read_line(&mut index)
        .expect("Failed to read line");

    let index: usize = index
        .trim()
        .parse()
        .expect("Index entered was not a number");

    let element = a[index];

    println!(
        "The value of the element at index {} is: {}",
        index, element
    );
}

当编译后运行时,输入的值为10时,报错如下:

thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

# 数组 # Rust