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 中的类型肯定远远不止这些的,其他更复杂的类型,我们在后续的学习过程中再来深入学习一下。

相关推荐
leiteorz1 小时前
第三章 Ownership与结构体、枚举
rust
初九之潜龙勿用2 小时前
技术与情感交织的一生 (十四)
笔记·印象笔记
love530love2 小时前
【笔记】 Podman Desktop 中部署 Stable Diffusion WebUI (GPU 支持)
人工智能·windows·笔记·python·容器·stable diffusion·podman
Olrookie2 小时前
若依前后端分离版学习笔记(十九)——导入,导出实现流程及图片,文件组件
前端·vue.js·笔记
初圣魔门首席弟子2 小时前
C++ STL string(字符串)学习笔记
c++·笔记·学习
CS_Zero2 小时前
【开发工具】Windows10&11远程Ubuntu18及以上桌面
笔记·ubuntu
iconball2 小时前
个人用云计算学习笔记 --18(NFS 服务器、iSCSI 服务器)
linux·运维·笔记·学习·云计算
肥肠可耐的西西公主3 小时前
后端(JavaWeb)学习笔记(CLASS 1):maven
笔记·学习·maven
kfepiza3 小时前
Spring的三级缓存原理 笔记251008
笔记·spring·缓存
DKPT6 小时前
JVM栈溢出和堆溢出哪个先满?
java·开发语言·jvm·笔记·学习