Rust 数据类型详解

一、标量类型(Scalar Types)

标量类型代表一个单独的值。Rust 中有四大基本标量类型:整数(integer)、浮点数(floating-point number)、布尔(boolean)和字符(character)。这几种类型在大多数编程语言中都很常见。

1. 整数类型(Integer Types)

整数(integer)是没有小数部分的数字。在之前的猜数字游戏教程里,我们用到了 u32。这个类型声明表示该值是一个无符号(unsigned)32 位整数(如果是有符号类型,会以 i 开头,例如 i32)。

下表展示了 Rust 中所有内置的整数类型,每个类型要么是有符号(signed),要么是无符号(unsigned),并且有明确的位数大小。

长度有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize
  • 有符号(signed)表示数值可能为正也可能为负,所以存储时需要符号位;
  • 无符号(unsigned)则只表示非负数(0 或正数),不需要符号位。

对于有符号整数,如果类型是 i8,它可以存储从 -128 到 127 的数值;若是 i16,则范围会相应扩大,以此类推。无符号类型则从 0 起算。例如 u8 能表示 0 到 255。

isizeusize 根据系统架构的不同而变化:在 64 位架构上是 64 位,在 32 位架构上是 32 位。这些类型常用于根据系统架构进行索引或内存大小计算等场景。

1.1 整数字面量

在 Rust 中可以使用多种形式来表达整数字面量(literal),如下表所示:

数字字面量形式示例
十进制98_222
十六进制0xff
八进制0o77
二进制0b1111_0000
字节(仅限 u8b'A'

注意:

  • 可以在数字中使用下划线 _ 作为分隔符来提高可读性,例如 1_0001000 等价。
  • 如果需要指定类型,可以在数字后面加上类型后缀,比如 57u8

通常如果不确定该用什么整数类型,Rust 默认使用 i32。若需要根据系统架构进行索引等场景时,才考虑使用 isizeusize

1.2 整数溢出(Integer Overflow)

假设我们有一个 u8 类型的变量,它能表示的数值范围是 [0, 255]。如果尝试将其赋值为 256,就会发生整数溢出integer overflow),导致以下两种行为之一:

  1. 调试(debug)模式编译:Rust 会执行溢出检查,一旦发现溢出,就会在运行时 panic(程序崩溃并退出)。
  2. 发布(release)模式编译:Rust 不做溢出检查,而是进行二补码环绕two’s complement wrapping)。换言之,超出最大可表示值时会“环绕”回最小值。例如,对于 u8 类型,256 会变成 0,257 会变成 1,等等。不会出现 panic,但是结果往往与期望不符。

在实际开发中,不应依赖整数溢出的环绕行为,这被认为是错误的做法。若需要显式处理溢出,可以使用标准库里为整数提供的以下方法族:

  • wrapping_*:如 wrapping_add,始终进行环绕运算;
  • checked_*:如 checked_add,若溢出则返回 None
  • overflowing_*:如 overflowing_add,返回一个元组 (结果, bool),其中 bool 指示是否发生溢出;
  • saturating_*:如 saturating_add,在溢出时结果会自动“饱和”到对应类型的最小或最大值。

2. 浮点数类型(Floating-Point Types)

Rust 提供了两种原生的浮点数类型:f32(32 位)和 f64(64 位)。默认使用 f64,因为在现代 CPU 上,f64f32 速度几乎相当,但精度更高。所有浮点类型都是有符号数。

fn main() {let x = 2.0;    // f64let y: f32 = 3.0;  // f32println!("x = {}, y = {}", x, y);
}

Rust 的浮点数遵循 IEEE-754 标准。

3. 数值运算(Numeric Operations)

Rust 支持常见的数值运算:加法、减法、乘法、除法和取余。需要注意的是,整数除法会向零方向取整(截断小数部分)。示例:

fn main() {// 加法let sum = 5 + 10;// 减法let difference = 95.5 - 4.3;// 乘法let product = 4 * 30;// 除法let quotient = 56.7 / 32.2;// 取余let remainder = 43 % 5;println!("sum = {}", sum);println!("difference = {}", difference);println!("product = {}", product);println!("quotient = {}", quotient);println!("remainder = {}", remainder);
}

如果需要查看 Rust 提供的所有运算符,可以参考 附录 B。

4. 布尔类型(Boolean Type)

布尔类型(bool)只有两个可能的值:truefalse。它所占的大小是 1 个字节。例如:

fn main() {let t = true;let f: bool = false;println!("t = {}, f = {}", t, f);
}

布尔常常用于条件判断(如 if 表达式),后面会在“控制流”一节详述。

5. 字符类型(Character Type)

Rust 的 char 类型是最基础的字母类型,用单引号包裹,支持 Unicode Scalar Value。这意味着它可以表示除 ASCII 之外更多的字符,比如带重音的拉丁字符、中文、日文、韩文、emoji、零宽空格等。例如:

fn main() {let c = 'z';let z = 'ℤ';let heart_eyed_cat = '😻';println!("{}, {}, {}", c, z, heart_eyed_cat);
}

Rust 的 char 类型占 4 个字节,对应 Unicode Scalar Value 范围:U+0000 ~ U+D7FFU+E000 ~ U+10FFFF。需要注意的是,Unicode 的“字符”概念可能与人们直觉中的“字符”不完全一致。详情可参考第 8 章关于字符串的讨论。

二、复合类型(Compound Types)

复合类型可以将多个值组合成一个类型。Rust 提供了两种原生的复合类型:元组(tuple)和数组(array)。

1. 元组类型(Tuple Type)

元组(tuple)可以将多个类型各异的值组合到一个复合类型中,长度固定,不可增长或缩短。使用小括号 () 包含并用逗号分隔不同的值。例如:

fn main() {let tup: (i32, f64, u8) = (500, 6.4, 1);println!("tup = {:?}", tup);
}
1.1 解构(Destructuring)元组

要获取元组中的单独值,可以使用模式匹配(pattern matching)进行解构:

fn main() {let tup = (500, 6.4, 1);let (x, y, z) = tup;println!("y = {}", y);
}

执行后,y 的值就是 6.4。这里 tup 被“拆解”成了 x, y, z 三个变量的过程,称为解构

1.2 使用索引访问元组

也可以直接用点号加索引来访问元组的指定元素:

fn main() {let x: (i32, f64, u8) = (500, 6.4, 1);let five_hundred = x.0;let six_point_four = x.1;let one = x.2;println!("{}, {}, {}", five_hundred, six_point_four, one);
}

需要注意,索引从 0 开始。

1.3 单元类型(Unit Type)

如果元组不包含任何元素,则被称为单元元组(unit)。它写作 (),表示一种空值或空的返回类型。若一个表达式没有返回任何其他值,默认会返回单元元组。

2. 数组类型(Array Type)

数组(array)也是一种把多个值组合在一起的方式,但它与元组有两个主要区别:

  1. 数组中所有元素类型相同
  2. 数组长度固定,一旦声明,长度就无法改变。

例如:

fn main() {let a = [1, 2, 3, 4, 5];println!("{:?}", a);
}

数组通常存储在上(stack)而不是堆上(heap),这在 第 4 章 会详细解释。若需要一个可伸缩的序列,则使用标准库提供的 向量vectorVec<T>)。如果你需要一个长度固定的序列,数组就非常合适。比如月份名称:

let months = ["January", "February", "March", "April", "May", "June","July", "August", "September", "October", "November", "December"
];
2.1 数组的类型注解

声明数组类型时,需要在方括号里写元素类型、分号、元素个数:

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

这里 i32 是每个元素的类型,5 表示数组长度。

2.2 初始化为相同元素

如果想让数组的所有元素都相同,可以使用如下语法:

let a = [3; 5];
// 等价于 let a = [3, 3, 3, 3, 3];
2.3 访问数组元素

可以使用索引来访问数组元素:

fn main() {let a = [1, 2, 3, 4, 5];let first = a[0];let second = a[1];println!("first = {}, second = {}", first, second);
}
2.4 越界访问与运行时错误

如果索引超出了数组的长度,Rust 会在运行时检查到错误并 panic:

fn main() {let a = [1, 2, 3, 4, 5];println!("请输入一个数组索引。");let mut index = String::new();std::io::stdin().read_line(&mut index).expect("读取失败");let index: usize = index.trim().parse().expect("输入的索引不是数字");let element = a[index];println!("你选择的元素是:{}", element);
}

如果你输入了超出 [0..4] 范围的索引,比如 10,就会引发 panic,显示类似:

thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19

程序因此退出并不会执行后续的 println!。这是 Rust 保证内存安全的体现:许多低级语言在越界索引时可能会访问非法内存地址,引发不可预料的后果,而 Rust 直接在运行时检测并退出以保证安全。

小结

在本篇文章中,我们介绍了 Rust 最常用的两种数据类型子集:标量类型复合类型。标量类型包括整数、浮点数、布尔和字符,它们各自有不同的表示和范围;复合类型包括元组和数组,可以用于将多个值组合到一个类型中,并且在长度是否可变和类型一致性方面有所区别。

  • 标量类型

    • 整数:如 i32, u32, i8, u8 等,不同字长和有符号/无符号选择;
    • 浮点数f32f64,默认使用 f64
    • 布尔bool,仅有 truefalse
    • 字符char,占 4 字节,可表示 Unicode Scalar Value。
  • 复合类型

    • 元组:可含多种类型,长度固定;可用解构或索引方式访问;
    • 数组:同类型元素的集合,长度固定,存储于栈上。

对于新手而言,遇到无法自动推断类型的情形时,需要加上类型注解,尤其是在使用 parse 或其他需要指明具体数值类型的场景下。随着实践的深入,Rust 提供的多种安全检查机制(如整数溢出检查、数组越界检查等)会给予你更多信心和安全感,同时也需要你熟悉这些机制以写出高效且安全的代码。

在后续章节中,我们将会不断深入 Rust 的特性,包括所有权、引用与切片、集合类型(向量、字符串、哈希映射)以及错误处理等,希望你能继续保持对 Rust 的探索与学习。

参考与致谢

  • The Rust Programming Language - By Steve Klabnik and Carol Nichols, CC BY 4.0
  • 本文部分内容基于其翻译和改写,如需了解更多细节,请阅读官方文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/4473.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OSI七层协议——分层网络协议

OSI七层协议&#xff0c;顾名思义&#xff0c;分为七层&#xff0c;实际上七层是不存在的&#xff0c;是人为的进行划分,让人更好的理解 七层协议包括&#xff0c;物理层(我),数据链路层(据),网络层(网),传输层(传输),会话层(会),表示层(表),应用层(用)(记忆口诀->我会用表…

【AI论文】生成式视频模型是否通过观看视频学习物理原理?

摘要&#xff1a;AI视频生成领域正经历一场革命&#xff0c;其质量和真实感在迅速提升。这些进步引发了一场激烈的科学辩论&#xff1a;视频模型是否学习了能够发现物理定律的“世界模型”&#xff0c;或者&#xff0c;它们仅仅是复杂的像素预测器&#xff0c;能够在不理解现实…

【TCP】rfc文档

tcp协议相关rfc有哪些 TCP&#xff08;传输控制协议&#xff09;是一个复杂的协议&#xff0c;其设计和实现涉及多个RFC文档。以下是一些与TCP协议密切相关的RFC文档列表&#xff0c;按照时间顺序排列&#xff0c;涵盖了从基础定义到高级特性和优化的各个方面&#xff1a; 基…

VLAN基础理论

VLAN V&#xff1a;Virtual(虚拟) LAN ——局域网 VLAN ——虚拟局域网(虚拟广播域&#xff1a;交换机和路由器协同工作后&#xff0c;将原来的一个广播域&#xff0c;逻辑上切分为多个。) VLAN的配置我们基于以下拓扑进行&#xff1a; PC1-4的IP地址依次为192.168.1.1-192.168…

RabbitMQ实现延迟消息发送——实战篇

在项目中&#xff0c;我们经常需要使用消息队列来实现延迟任务&#xff0c;本篇文章就向各位介绍使用RabbitMQ如何实现延迟消息发送&#xff0c;由于是实战篇&#xff0c;所以不会讲太多理论的知识&#xff0c;还不太理解的可以先看看MQ的延迟消息的一个实现原理再来看这篇文章…

IoTDB 常见问题 QA 第四期

关于 IoTDB 的 Q & A IoTDB Q&A 第四期来啦&#xff01;我们将定期汇总我们将定期汇总社区讨论频繁的问题&#xff0c;并展开进行详细回答&#xff0c;通过积累常见问题“小百科”&#xff0c;方便大家使用 IoTDB。 Q1&#xff1a;Java 中如何使用 SSL 连接 IoTDB 问题…

【STM32-学习笔记-14-】FLASH闪存

文章目录 FALSH闪存一、FLASH简介二、FLASH基本结构三、FLASH解锁四、使用指针访问存储器五、FLASH擦除以及编程流程Ⅰ、程序存储器全擦除1. 读取FLASH_CR的LOCK位2. 检查LOCK位是否为13. 设置FLASH_CR的MER 1和STRT 1&#xff08;如果LOCK位0&#xff09;4. 检查FLASH_SR的B…

CamemBERT:一款出色的法语语言模型

摘要 预训练语言模型在自然语言处理中已无处不在。尽管这些模型取得了成功&#xff0c;但大多数可用模型要么是在英语数据上训练的&#xff0c;要么是在多种语言数据拼接的基础上训练的。这使得这些模型在除英语以外的所有语言中的实际应用非常有限。本文探讨了为其他语言训练…

线性代数概述

矩阵与线性代数的关系 矩阵是线性代数的研究对象之一&#xff1a; 矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合&#xff0c;是线性代数中的核心概念之一。矩阵的定义和性质构成了线性代数中矩阵理论的基础&#xff0c;而矩阵运算则简洁地表示和…

金仓Kingbase客户端KStudio报OOM:Java heap space socketTimeout

找到Kingbase\ES\V8\KESRealPro\V008R006C006B0021\ClientTools\guitools\KStudio\KStudio.ini 修改JVM参数&#xff1a; 默认值&#xff1a; -Xms512m -Xmx1024m 改为&#xff1a; -Xms1024m -Xmx2048m -XX:MaxPermSize512m SQL查询报错&#xff1a;An I/O error occurred …

Spring6.0新特性-HTTP接口:使用@HttpExchange实现更优雅的Http客户端

文章目录 一、概述二、使用1、创建接口HttpExchange方法2、创建一个在调用方法时执行请求的代理3、方法参数4、返回值5、错误处理&#xff08;1&#xff09;为RestClient&#xff08;2&#xff09;为WebClient&#xff08;3&#xff09;为RestTemplate 注意 一、概述 官方文档…

kubernetes学习-Service(七)

一、Service-pod-endpoint关系 # 查看endpoints [rootk8s-master deployments]# kubectl get endpoints NAME ENDPOINTS AGE kubernetes 192.168.129.136:6443 90m nginx-svc 10.109.131.1:80,10.111.156.65:80 22m # …

Python数据分析案例70——基于神经网络的时间序列预测(滞后性的效果,预测中存在的问题)

背景 这篇文章可以说是基于 现代的一些神经网络的方法去做时间序列预测的一个介绍科普&#xff0c;也可以说是一个各种模型对比的案例&#xff0c;但也会谈一谈自己做了这么久关于神经网络的时间序列预测的论文&#xff0c;其中一些常见的模式及它们存在的问题以及效果&#x…

opencv笔记2

图像灰度 彩色图像转化为灰度图像的过程是图像的灰度化处理。彩色图像中的每个像素的颜色由R&#xff0c;G&#xff0c;B三个分量决定&#xff0c;而每个分量中可取值0-255&#xff0c;这样一个像素点可以有256*256*256变化。而灰度图像是R&#xff0c;G&#xff0c;B三个分量…

LeetCode:2266. 统计打字方案数(DP Java)

目录 2266. 统计打字方案数 题目描述&#xff1a; 实现代码与解析&#xff1a; 动态规划 原理思路&#xff1a; 2266. 统计打字方案数 题目描述&#xff1a; Alice 在给 Bob 用手机打字。数字到字母的 对应 如下图所示。 为了 打出 一个字母&#xff0c;Alice 需要 按 对…

http://noi.openjudge.cn/——4.7算法之搜索——【169:The Buses】

题目 169:The Buses 总时间限制: 5000ms 内存限制: 65536kB 描述 A man arrives at a bus stop at 12:00. He remains there during 12:00-12:59. The bus stop is used by a number of bus routes. The man notes the times of arriving buses. The times when buses arrive …

java基础概念59-File

一、路径 二、File类 2-1、常见的构造方法 示例&#xff1a; 【注意】&#xff1a; 一般不自己用分割符把父路径和子路径拼接起来&#xff0c;因为&#xff0c;不用的操作系统&#xff0c;分隔符不同。 2-2、小结 2-3、File中常见的成员方法 示例&#xff1a; 【注意】&#…

PortSwigger靶场练习---第二关-查找和利用未使用的 API 端点

第二关&#xff1a;Finding and exploiting an unused API endpoint 实验&#xff1a;查找和利用未使用的 API 端点 PortSwigger靶场地址&#xff1a; Dashboard | Web Security Academy - PortSwigger 题目&#xff1a; 官方提示&#xff1a; 在 Burp 的浏览器中&#xff0c…

软路由系统iStoreOS 一键安装 docker compose

一键安装命令 大家好&#xff01;今天我来分享一个快速安装 docker-compose 的方法。以下是我常用的命令&#xff0c;当前版本是 V2.32.4。如果你需要最新版本&#xff0c;可以查看获取docker compose最新版本号 部分&#xff0c;获取最新版本号后替换命令中的版本号即可。 w…

SpringCloud nacos 2.0.0 + seata 2.0.0

NACOS 下载nacos https://github.com/alibaba/nacos/releases/tag/2.2.0 启动nacos startup.cmd -m standalone SEATA 下载seata https://seata.apache.org/release-history/seata-server 新建数据库-seata CREATE TABLE branch_table (branch_id bigint NOT NULL,xid …