06-数据类型

前言

在上一章当中,我们以变量与可变性作为切入点,正式敲开了 Rust 的语法学习之门,了解了在 Rust 当中如何定义变量、可变变量、常量,以及了解了 Rust 中的变量隐藏的概念以及它的使用场景。那么在这一章里,我们就来逐步学习一下 Rust 当中的各种数据类型。

Rust 是一门静态的语言,这就意味着,Rust 在编译的过程中,需要知道所有的变量的类型。而在大部分情况下,Rust 都可以基于变量使用的值推断出这个变量应该是什么类型的。但也有一些特殊情况,就拿我们之前开发的的 猜数游戏 小程序当中,我们将一个字符串转换成数字的方法 parse 来说,这个方法返回的类型可能是很多种的,如:布尔类型、整型(i32u32i64)、浮点型等等,此时,Rust 也不知道这个变量应该是什么类型了,这个时候,就需要我们显式的标注出目标类型,否则编译器会报错 。

标量类型

一个标量类型代表了一个单个的值。在 Rust 当中有四种标量类型,分别是:

整数类型

整数类型是没有小数部分的,只能赋值为整数,整数类型也可以分为很多种:

大概分为有符号整数类型(以i开头)和无符号整数类型(以u开头),而有根据所占空间的大小可以分为:

有符号整数的取值范围:-(2 ^ n - 1) ~ (2 ^ n -1)

无符号的整数取值范围:0 ~ 2 ^ n -1

上面的 n 就是代表所占的位数。

上面表中最后一个需要特殊说明一下,arch 代表的是系统的架构,也就是说,isizeusize 会根据系统架构不同而改变,在 32 位的系统中是 i32,而在 64 位的系统中,则是 i64,举个例子:

rust 复制代码
let str = "kiner";
let str_len = str.len();

我们定义了一个字符串类型的变量 str,然后有定义了一个用于保存字符串长度类型的变量 str_len,而 str_len 的值是调用字符串的 len的方法获取字符串的长度,此时,这个方法返回的长度的类型就是 usize,为啥用 usize,而不是isize呢?你见过字符串的长度是负数的么?字符串长度是不是最小只能是 0,因此没必要使用有符号整数,无符号整数就够用了,因此使用的是 usize。而我们上面说了,isizeuszie 占的空间大小是取决于当前设备的系统架构的,如果当前运行设备是 64 位的,那么这里的 usize 的大小就相当于 u64,如果是 32 位的,就相当于是 u32 的大小了。

字面值

  • 十进制 :如:10_000,可以使用 _进行格式化分隔,便于人眼识别

  • 十六进制 :如:0xff 转换成十进制就是 255

  • 八进制 :如:0o77,转换成十进制就是 63

  • 二进制 :如:0b1100_0011,转换成十进制就是 195

  • 字节表示,仅限于 u8 类型 :如:b'A',十进制表示就是 65,相当与获取字符"A"的 charCode。类比到 TS/JS中就相当于是 'A'.charCodeAt(0)

类型后缀

Rust 当中,上面所有的标识形式的字面值,都可以减伤类型后缀用于标识类型,如:

当然,你也可以在类型后缀和数值之间不加 _,但这样可读性就变得很差了,个人建议还是要加类型后缀时,使用 _ 将值与类型分隔开,这样可读性就更高了。

默认类型

Rust 当中,整数有那么多个子类型,但在很多情况下,你不知道该如何选择用那种类型,那这个时候,你可以考虑使用推荐的默认类型:i32,这个类型总体上来说,即使实在 64 位的系统中也是速度很快的。

整数的溢出

我们上面说了有符号整数和无符号的整数的取值范围:

有符号整数的取值范围:-(2 ^ n - 1) ~ (2 ^ n -1)

无符号的整数取值范围:0 ~ 2 ^ n -1

如,一个 u8 类型的整数,他的取值范围是 0 ~ 255 的,那假如说我们给它赋值:256 会怎样呢?我们直接来试一下:

不出意料的,如果我们这么赋值,Rust 会直接给我们一个无情地报错,并提示我们,取值范围应该是 0~255的全闭区间

浮点类型

Rust 当中,跟 java 等高级语言一样,有两种浮点类型:

  • f32:单精度浮点
  • f64 :双精度浮点,类似与 java 中的 double

默认类型

因为在现代的 cpu 上, f32f64 的运算速度是差不多的,为了能够精度更高,采用 f64 作为浮点类型的默认类型。

布尔类型

跟其他语言一样,只有 truefalse 两个值,跟其他语言不一样的是,其他语言如 ts ,布尔类型表示为:boolean,而在 Rust 当中,则是表示为:bool,并且进占用一个字节的大小。

字符类型

使用 char 类型来描述语言中最基础的单个字符的类型。字符类型的字面值使用单引号,并且占用 4 个字节。

rust 复制代码
let a: char = 'a';
// 这样会报错
let b char = "a";

复合类型

复合类型就是可以把多个类型放在一个类型里面,在 Rust 里面,提供了两种基础的复合类型,包括:元组(Tuple)、数组。

这两个其实在 TS/JS 中也有,而且很常用,如:

tsx 复制代码
function Comp (){
  // 这个就是一个由两个元素组成的元组,第一个元素的类型取决于 useState 中传入的初始值的类型 T,而第二个元素的类型则是:Dispatch<SetStateAction<T>>
  const [showModal, setShowModal] = useState(false);
  const arr: (string | number)[] = ['kiner', 0];
}

元组(Tuple)

元组可以将多个类型放在一个类型里面,并且元组的长度一旦初始化之后就是固定不能更改的。

声明元组

前端同学应该都很清楚在 TS/JS 当中应该如何声明一个元组:

typescript 复制代码
// 匿名声明
const tuple1: [number, string] = [0, 'kiner'];
// 具名声明
const tuple2: [age: number, name: string] = [18, 'kiner'];

那么,在 Rust 当中应该如何声明元组呢?

Rust 当中,使用的是小括号来声明元组,这与 TS/JS 中用中括号声明是有区别的,可以在每个位置上都声明一个对应的类型,而且这些类型不要求一定要相同的,我们直接来看一个例子:

rust 复制代码
let tuple = (0,'a',"kiner",3.0_f32, 2.88_f64);

获取元组中的值

在学习 Rust 中的元组如何取值之前,我们先来回想一下,在 TS/JS 当中我们应该如何取值呢?

typescript 复制代码
const tuple2: [age: number, name: string] = [18, 'kiner'];
// 1. 使用解构的方式获取元组中的值
const [age, name] = tuple2;
console.log(`My name is ${name},I'm ${age} years old!`);
// 2. 使用索引取值
console.log(`My name is ${tuple2[0]},I'm ${tuple2[1]} years old!`);

其实在 Rust 当中的取值也是很相似的:

rust 复制代码
let tuple = (0,'a',"kiner",3.0_f32, 2.88_f64);
// 使用结构法获取元素值
let (a, b, c, d, e) = tuple;
println!("a: {},b: {},c: {},d: {},e: {}",a,b,c,d,e);
// 使用索引取值
// 跟 TS/JS 不一样的是,Rust 不是用 [索引] 这种方式,而是 .索引 的方式取值
println!("a: {},b: {},c: {},d: {},e: {}",tuple.0,tuple.1,tuple.2,tuple.3,tuple.4);

数组

数组也是可以将多个类型放在一个类型里面,但是与元组不同的是,在 Rust 当中,数组中每一个元素的类型都是一样的。(这与 TS/JS 当中也是有区别的,在 TS/JS 中是允许数组中每个元素的类型不一样的)。并且在 Rust 当中,数组的长度在声明之后也是固定的,不能变长,也不能缩短,这也是跟 TS/JS 中的数组的重要区别点之一。

数组的作用

Rust 当中,数组是很常用且有用的数据结构,它的应用场景如下:

  • 数据存放在栈内存:当你想让你的数据存放在栈内存,而非堆内存时,就可以使用数组,因为数组是在 栈内存(Stack)分配的单块的内存。
  • 保证固定数量的元素 :由于在 Rust 当中,数组一旦声明之后,长度就不可变了,因此,如果在某些场景下,我们想要确保获得固定数量的元素时,可以使用数组。

Vector

或许有同学会说,数组不能够动态伸缩了,这样在很多场景很不方便,有没有其他解决方法呢?

在 Rust 中,Vector 是一种动态数组类型,它可以在运行时进行大小的调整。Vector 提供了一些方法来方便地操作和管理数据。

Vector 的作用:

  1. 动态大小:Vector 允许在运行时动态调整大小,可以根据需要添加或删除元素。
  2. 所有权管理:Vector 可以拥有其元素的所有权,这意味着它负责在适当的时间释放内存。
  3. 灵活性:Vector 可以存储任何类型的元素,并且可以轻松地在不同元素类型之间进行转换。

Vector 和数组的区别:

  1. 大小:数组在编译时就确定了其大小,而 Vector 可以在运行时根据需要动态调整大小。
  2. 所有权:数组的所有元素拥有相同的所有权属性,而 Vector 的元素可以拥有不同的所有权属性。这意味着 Vector 可以存储任意不同所有权类型的元素。
  3. 可变性:数组是固定大小的,并且默认情况下是不可变的,而 Vector 是可变的,并且可以通过 pushpop 等方法在运行时添加或删除元素。

以下是一个示例,演示了 Vector 和数组的使用:

rust 复制代码
fn main() {

    // 使用数组
    let arr: [i32; 3] = [1, 2, 3]; // 声明一个包含 3 个 i32 类型元素的数组
    println!("Array: {:?}", arr);


    // 使用 Vector
    let mut vec: Vec<i32> = vec![1, 2, 3]; // 声明一个包含 3 个 i32 类型元素的 Vector
    println!("Vector: {:?}", vec);



    // 添加元素到 Vector
    vec.push(4);
    vec.push(5);
    println!("Updated Vector: {:?}", vec);



    // 删除 Vector 中的元素
    vec.pop();
    println!("Updated Vector after pop: {:?}", vec);

}

在上述示例中,我们声明了一个包含 3 个元素的数组 arr 和一个包含 3 个元素的 Vector vec。然后,我们使用 push 方法向 Vector 添加两个元素,并使用 pop 方法删除一个元素。

总结起来,Vector 在 Rust 中提供了动态大小、所有权管理和灵活性等特性,使其在需要动态调整大小的场景中非常有用。与之相比,数组在编译时就确定了大小,是一个固定大小的数据结构。当你不知道该使用数组还是使用 Vector 时,建议使用 Vector

数组的类型

Rust 当中,我们可以这么定义一个数组:

rust 复制代码
// 其中,在中括号包含了两部分的信息,分号左边的是数组元素的类型,而分号右边则是数组的一个长度
let arr: [i32;5] = [1,2,3,4,5];

除了上面这种最常见的声明方式外,还可以这样声明:

rust 复制代码
// 我们直接在赋值的时候,给数组一个初始值 false,然后分号后面跟着长度,就代表这个长度为 5 的数组里面所有的元素初始值都是 false
let arr = [false;5];

访问数组的元素

访问数组元素的方式其实跟其他语言是差不多的

TS/JS 不一样的是,如果我们访问数组越界了,在 Rust 会报错:

结语

至此,我们已经相对详细的学习了在 Rust 当中的一些基础的标量类型和复合类型了。当然,Rust 中的类型肯定远远不止这些的,其他更复杂的类型,我们在后续的学习过程中再来深入学习一下。

相关推荐
yuwinter9 分钟前
鸿蒙HarmonyOS学习笔记(2)
笔记·学习·harmonyos
游走于计算机中摆烂的1 小时前
启动前后端分离项目笔记
java·vue.js·笔记
fqbqrr2 小时前
2411rust,实现特征
rust
码农飞飞2 小时前
详解Rust枚举类型(enum)的用法
开发语言·rust·match·枚举·匹配·内存安全
你可以叫我仔哥呀2 小时前
ElasticSearch学习笔记三:基础操作(一)
笔记·学习·elasticsearch
maxiumII2 小时前
Diving into the STM32 HAL-----DAC笔记
笔记·stm32·嵌入式硬件
美式小田5 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
猫爪笔记5 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
_不会dp不改名_5 小时前
HCIA笔记3--TCP-UDP-交换机工作原理
笔记·tcp/ip·udp
-一杯为品-6 小时前
【51单片机】程序实验5&6.独立按键-矩阵按键
c语言·笔记·学习·51单片机·硬件工程