跳转至

数据类型

一、概述

Rust的数据类型包含两个大类,即:标量和复合类型。Rust是静态语言,在编译时必须知道所有变量类型

  • 基于使用的值,编译器通常能够推断出它的具体类型

  • 但如果可能的类型比较多,就必须添加类型声明标注,否则会编译报错。例如把String调用parse方法转为整形,我们必须标注接受值的变量为整形,是因为该方法能推断的类型种类比较多,如:i32u32等。

我们可以使用以下示例代码声明一个整形变量,用于存储通过解析字符串得到的值。这样rust就知道我们要解析的值确定是u32类型

fn main() {
    let guess: u32 = "42".parse().expect("Not a number!");
    println!("You guessed: {}", guess);
}

二、标量类型

  • 一个标量类型代表一个单个的值

  • Rust有四个主要的标量类型

  • 整数类型

  • 浮点类型

  • 布尔类型

  • 字符类型

2.1 整数类型

整数类型 整数类型没有小数部分,例如u32就是一个无符号的整数类型,占据32位的空间。无符号整数类型以u开头,有符号的数据类型以i开发,rust的整数类型列表如下图

长度 有符号类型 无符号类型
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize

有符号的数字类型有正有负,无符号的数字类型只有正数。

有符号范围:-(2^n - 1) 到 2^(n-1) - 1

无符号范围:2^n - 1

arch类型由计算机的架构位数所决定,如果计算机是64位,那么isizeusize的位数就是64。使用isizeusize的主要场景是对某种集合进行索引操作。

整数字面值 整数的字面值,可以有多种表示形式,如下表

字面值 示例
十进制(decimal) 98_222
十六进制(hex) 0xff
八进制(Octal) 0o77
二进制(Binary) 0b1111_0000
字节(仅限与u8类型) b'A'

出了byte类型之外,所有的数值类型都允许使用类型后缀,例如:57u8

如果不清楚应该使用哪种类型,可以使用rust相应的默认类型,整数的默认类型是i32,总体上来说速度很快,即使在64位系统中

2.1.1 整数溢出

例如:u8的范围是0-255,如果你把一个u8变量的值设为256,那么:

  • 在调试模式下编译,rust会检查整数溢出,如果发生溢出,程序在运行时就会发生panic
  • 在发布模式下(--release)编译,rust不会检查可能导致panic的整数溢出,如果发生溢出,rust会执行环绕操作,256变成0,257变成1,以此类推,但不会导致程序的panic

2.2 浮点类型

rust有两种基础的浮点类型,也就是含有小数部分的类型,包含以下两种

  • f32,占据32位长度,单精度

  • f64,占据64位长度,双精度

rust的浮点数类型使用了 IEEE-754 标注来描述,f64 是默认类型,因为在现代计算机里上f64和f32运行速度差不多,但f64更加精确。

2.3 数值的操作

  • 数值支持加、减、乘、除、余计算,

2.3 布尔类型

rust的布尔类型也有两个值:true和false,占用一个字节的大小

2.4 字符类型

rust语言中char类型被用来描述语言中最基础的单个字符,字符类型的字面值使用单引号,占用4个字节的大小,字符类型是Unicode标量值,可以表示比 ASCII 多很多的字符内容:拼音、中文、日文、韩文、零长度空白字符、emoji表情等。范围值是

  • U+0000 到 U+D7FF

  • U+E000 到 U+10FFFF

但是,Unicode 中并没有“字符”概念,所以直觉上认为的字符也许与rust中的概念不相符。

三、复合类型

复合类型可以将多个值放在一个类型了,rust提供两种基础的复合类型:元组(tuple)、数组(数组)

3.1 元组

Tuple可以将多个类型的多个值放在一个类型里,Tuple的长度是固定的,一旦声明后就无法改变。创建Tuple的过程如下

  • 在小括号里,将值用都好隔开

  • Tuple中的每个位置都对应一个类型,Tuple中各元素的类型不必相同

3.1.1 声明元组

以下创建一个Tuple类型数据,并将数据内容输出到终端上

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let pat = expr;
}

3.1.2 获取tuple的元素值

可以使用模式匹配来结构(destructure)一个Tuple来获取元素的值。例子:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;

    println!("The value of x, y z, is: {},{},{}", x, y, z);
}

3.1.3 访问tuple的元素

在rust中使用点标记法,后接元素索引号来访问tuple元素,如下

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);

    println!("The value of tup items is: {},{},{}", tup.0, tup.1, tup.2);
}

3.2 数组

数组也可以将多个值放在一个类型里,但数组中每个元素的类型必须相同,数组的长度也是固定的,一旦声明之后,不可以改变。

声明数组 在中括号里,各个值使用都好隔开,如下例子

let a = [1, 2, 3, 4, 5];

以下再定义一个包含所有月份的数组,并打印第一个元素

fn main() {
    let months = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    ];

    print!("the first item is {}", months[0]);
}

3.2.2 数组的用处

如果想让数据存储在栈(stack)内存上,而不是堆(heap)内存上,或者想保证有固定数量的元素,这时使用数组更有号去处。

数组是由预导入模块(prelude)提供,不过prelude也是标准库的一部分,的可以说标准库式prelude的超集。

实际上rust标准库还提供了一个和数组类型的数据类型--vector,数组没有vector灵活,vector的长度可以改变。如果我们不确定使用数组还是使用vector,这时候应当使用vector。

3.2.3 数组的类型

数组的类型以及长度以下面这种形式表示[类型;长度],如下代码

let a:[i32; 5] = [1, 2, 3, 4, 5];

如果数组的每个元素都相同,那么我们可以使用以下的声明方式

let a = [3; 5];

它就相当于以下表达式

let a = [3, 3, 3, 3];

3.2.4 数组的访问

数组是stack上分配的单个块内存,可以使用索引来访问数组元素,如下读取months这个数组的第一个元素

let first = months[0];

rust不允许继续访问其数据相邻的地址的内存,运行的时候会报数组越界的错误(runtime时会panic)。如果访问的数组索引超出了数组的范围,编译的时候可能不会通过,因为rust回对编译的代码进行简单的检查,如下

fn main() {
    let months = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    ];

    let index = 15;

    print!("{}", months[index]);
}

以上代码编译时将会报错,因为rust通过简单的判断能判定处months元素个数只有12个,使用15这个索引值访问是不被允许的。但如果我们把逻辑变得更加复杂,rust就没法在编译时进行检查了,只能在运行时进行代码检查,执行过程中逻辑出错rust将会报错并退出程序。如下代码

fn main() {
    let months = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    ];

    let index = [15, 16, 17];

    print!("{}", months[index[0]]);
}

因为有了编译时简单逻辑检查,也使得我们的程序更加安全。