使用默认不可变的Rust变量会踩什么坑

讲动人的故事,写懂人的代码

Rust的变量真的是名不副实。名字中明明有个“变”字,却默认不可变。还美其名曰“不可变变量”。要想让变量名副其实,还必须费心额外加个mut关键字,并必须称其为“可变变量”,才能与前者区分开。这两个名字越琢磨越有趣。

与名不副实的变量相关的概念还真不少。

  • 声明、初始化和绑定变量的语句
  • 可用于变量赋值的一般表达式与控制流表达式
  • 变量的数据类型
  • 可以接受变量作为参数并能将返回值赋给变量的函数
  • 与变量一样都可以存储值的常量

变量名不副实这一点足以让程序员踩坑,而与变量相关的那些概念也暗藏着不少陷阱。那么,程序员在使用Rust的变量及其相关概念时,最容易在哪些场景中踩坑呢?

在这里插入图片描述

3.1 不可变变量绑定值后再为其赋值

对于Rust语言之外的其他编程语言来说,变量默认是可变的。这一点从变量的名字就显而易见。于是不少有其他语言使用背景的初学者,经常踩误为不可变变量赋值的坑。

3.1.1 在循环中误为不可变变量赋值

在循环中求和,是常见的计算方法。当如果忘记Rust的变量默认不可变,那么就会踩为不可变变量赋值的坑,如代码清单3-1所示。

为节省篇幅,本书大部分代码清单只展示最关键的代码(从不连续的行号能看出来)。完整代码可以在用git下载代码后,在代码清单开头注释中标明的源代码位置中查看。

本书代码下载链接为github.com/wubin28/book_LRBACP。本书所有的代码清单,会注明在这个链接中的文件夹位置,以便读者找到相应的没有行号的代码来运行。

下载代码之前,请先安装git。具体的安装步骤,可以询问你最喜欢用的生成式AI聊天工具。

之后,可以运行git clone命令,然后进入文件夹book_LRBACP即能看到所有代码。

代码清单3-1 在循环中误为不可变变量赋值

// 源代码位置:ch03/immutable_misstep3     let sum = 0;4     for i in 1..=3 {5         // sum += i;  // 取消注释这行以查看编译错误7     }

代码清单3-1所对应的完整源代码,展示了如何正确和错误地使用变量来计算1到3的累加和。代码通过三种不同的方式来阐述这个问题,突出了"不可变变量绑定值后再误为其赋值"的主旨。限于篇幅,书中只展示和解释重要的代码片段。对于完整源代码中不明白的语句,读者可以自行用最喜欢的生成式AI来解释。

第3行声明了一个不可变变量sum并初始化为0,也就是将0绑定到不可变变量sum上。这里是"误用不可变变量"问题的开始。

第4-7行使用for循环遍历1到3的范围。

第4行是Rust中的一个for循环语句。for 关键字表明要开始一个循环结构。i是循环变量。在每次迭代中,i 会被赋予范围中的下一个值。in这个关键字用来指定循环将遍历一个范围或集合。1..=3是一个范围表达式,它定义了循环将要遍历的值。.. 是Rust的范围语法。1..3 将创建一个不包含上界的范围,即 1 和 2。1..=3 中的 = 符号表示这是一个包含上界的范围。{这个大括号标志着循环体的开始。循环体中的代码将对范围中的每个值执行一次。所以,第4行完整含义是创建一个循环,其中变量 i 将依次取值 1、2 和 3。对于每个值,执行循环体中的代码。

第5行就踩坑里了。如果将第5行的注释去掉,那么这行代码就是其他主流编译语言通常的做法:用赋值的方法试图修改sum。但由于sum是不可变的,这会导致编译错误。

❗️变量避坑指南

不可变变量一旦绑定,就不能再赋值。

如何修复这个问题?代码清单3-1所对应的完整源代码展示了两种方法。一种是在第3行变量sum前,添加mut关键字,使其成为可变变量,这样把第5行的注释取消,编译就不再报错。另一种方法是使用函数式编程的方法,即只用let sum: i32 = (1..=3).sum();这一句,不仅能完成求和与sum的变量绑定工作,还不必把sum声明为mut。这样既省事,代码可读性也好了不少。

讲了半天变量,到底什么是Rust的变量?

❓什么是Rust的变量

Rust的变量是一个命名了的存储位置,它绑定了一个内存中的值,并遵循Rust的所有权规则和生存期规范。

具体来说,Rust的变量有一个标识符(名称),用于在代码中标识它。变量与一个特定的值相关联。这种关联在Rust中被称为"绑定"。变量代表了内存中存储的数据。每个值在任一时刻只能有一个所有者(即变量)。当变量离开作用域时,它所拥有的值会被自动清理。变量的生存期受到严格控制,确保在使用时始终有效。变量命名使用snake_case风格(即单词全小写,单词之间用下划线分隔)。作用域是变量在代码块中可以访问的范围,通常是从声明点开始到包含它的代码块结束,由大括号 {} 界定。

此外,Rust变量还有以下特征。

  • 默认不可变。除非明确声明为可变。不可变变量一旦被绑定就不能更改其值。
  • 类型安全。每个变量都有一个在编译时确定的类型,即使是通过类型推断确定的。
  • 作用域限制。变量的可见性和生存期通常限于声明它的代码块。
  • 支持遮蔽(详见3.3)。可以在同一作用域内多次声明同名变量,新变量会遮蔽旧变量(即旧变量失效)。

上面提到,代码清单3-1的第3行既有变量sum的声明,又有初始化,还提到了绑定。第5行还有赋值。那么变量的声明、初始化、绑定和赋值之间有什么联系和区别?

❓变量的声明、初始化、绑定与赋值

在Rust中,变量的声明、初始化、绑定与赋值是密切相关的概念,它们有一些细微的区别和特定的含义。

变量声明是在程序中引入一个新的变量名。在Rust中,变量声明通常使用 let 关键字。如下所示。

let x;  // 变量声明

变量初始化是给变量赋予一个初始值的过程。在Rust中,初始化通常在声明的同时完成。初始化标志着变量生存期的开始。变量的生存期,指变量从完成声明和初始化开始,到变量因所有权移动、被显式释放或离开作用域而结束的这段时间。

如下所示。

let x = 5;  // 变量声明并初始化,即创建一个绑定

❗️变量初始化避坑指南

变量只能被初始化一次。

**变量绑定结合了声明和初始化的概念。**在Rust中,变量"绑定"这个术语更为常用。当"绑定一个变量"时,通常指的是声明一个变量并将其与一个值关联起来。如上所示。上面这行代码将变量名 x 绑定到值 5 上。

在很多语言中,变量可以先声明后初始化。在Rust中,虽然可以将变量的声明和初始化分开(适用于变量在声明时无法立即确定其值,或变量的初始值需要通过某些计算或函数调用而得到的场景),但在使用变量之前,必须确保它已被初始化。Rust编译器会跟踪变量是否被初始化,以确保在使用前已经初始化。如下所示。

let x;      // 声明不可变变量x
x = 5;      // 初始化x,貌似为不可变变量赋值,但其实不是
println!("{}", x);  // 使用

❗️变量初始化避坑指南

当变量的声明和初始化分开时,初始化不要求变量是可变的。

**赋值是将一个新值存储到已经声明并初始化的可变变量中的过程。**可以多次进行赋值。赋值操作不会改变变量的类型。赋值可以发生在变量生存期内的任何时候。如下所示。

let mut x = 5;
x = 10; // 赋新值

❗️变量赋值避坑指南

只有可变变量才能被赋值。

在Rust中,绑定不仅仅是声明和初始化。它还涉及所有权(ownership)的概念。当绑定一个值到变量时,该变量成为这个值的唯一所有者。

Rust允许重新绑定同名变量,这被称为"遮蔽"(详见3.3)。

默认情况下,Rust中的绑定是不可变的。要创建可变绑定,需要使用 mut 关键字。如下所示。

let mut y = 5;  // 可变绑定
y = 6;          // 允许用赋值语句修改

Rust在绑定时可以进行类型推断,但也允许显式指定类型。如下所示。

let z = 5;       // 整型类型推断默认为 i32
let w: f64 = 5.0;  // 显式指定类型64位浮点数

在Rust中,绑定有明确的生存期,通常持续到变量离开作用域后结束。

变量绑定和赋值可能会涉及所有权的转移,特别是对于非复制(non-Copy)类型的值。

在这里插入图片描述

3.1.2 误为不可变结构体字段赋值

**结构体是Rust中用于创建自定义数据类型的一种方式。**它允许程序员将多个相关的值组合成一个有意义的组。当需要改结构体内某个字段的值的时候,会踩什么可变性的坑?代码清单3-2就是一个踩坑的例子。

代码清单3-2 误为不可变结构体字段赋值

// 源代码位置:ch03/immutable_field_mishap1 struct Point {2     x: i32,3     y: i32,4 }5 6 fn main() {8     let point = Point { x: 0, y: 0 };
10 
11     // point.x = 5;  // 取消注释这行以查看编译错误
42 }

代码清单3-2所对应的完整源代码,演示了三种情况:不可变结构体字段的赋值错误、使用可变结构体正确修改字段,以及使用RefCell实现内部可变性。代码的主旨是展示"误为不可变结构体字段赋值"的问题及其解决方法。

第1-4行定义了一个名为Point的结构体,包含两个i32类型的字段xy

第8行创建一个不可变的Point实例point,初始化xy坐标为0。这是踩坑的起点。

第11行踩坑了。这行被注释掉的代码试图用赋值,修改不可变结构体实例pointx坐标,如果取消注释,将导致编译错误。

如何修复这个问题?代码清单3-2所对应的完整源代码,给出了两种修复方法。

第一种方法是在第8行实例point前面,添加mut关键字,使其变为可变实例。

❗️结构体可变性避坑指南

默认情况下,结构体实例是不可变的。要创建可变的结构体实例,需要在声明结构体变量时使用 mut 关键字。结构体的可变性是整体的,不能只将某个字段标记为可变。

第二种方法是在保持point实例不可变的情况下,将其用智能指针RefCell<T>包裹起来。然后利用RefCell<T>的内部可变性,来改变不可变结构体实例point内部字段的值。

❗️在不可变上下文中改变数据的避坑指南

一个不可变变量所拥有的的数据,并不是完全不能修改。使用内部可变性,是能够实现在不可变上下文中改变数据的。内部可变性是 Rust 中的一种设计模式,它允许程序员在拥有不可变引用、不可变变量或不可变实例时改变数据。这看似违反了 Rust 的借用规则,但实际上并不是这样。内部可变性是在语言的安全保证内提供了一种受控的方式来实现可变性。RefCell<T>Cell<T>Mutex<T>RwLock<T>是实现内部可变性的常用智能指针类型。

如果喜欢这篇文章,别忘了给文章点个“赞”,好鼓励小吾继续写哦~😃

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【AI人工智能】文心智能体,陪爸妈去旅游,国庆假期不容错过,旅游搭子首选

文章目录 背景创作灵感陪爸妈去旅游简介角色与目标思考路径个性化开场白调优 智能体体验总结和感受 背景 文心智能体平台&#xff0c;开启新一轮活动&#xff0c;超级创造营持续百日活动。 在AI 浪潮席卷的今天&#xff0c;如雨后春笋般丛生的 AI 应用&#xff0c;昭告着时代风…

MySQL优化实战 解决CPU100%

问题表象 在24年初有一个日经问题困扰着我们&#xff0c;每到正点03分DB的CPU开始打满&#xff0c;持续1分钟又恢复正常水平。但由于日常业务交付压力较大且权限限制没有登录DB主机的权限&#xff0c;大家也就得过且过一直没有去认真排查。直到某天我来兴趣了也有时间了&#…

基于51单片机的家用防火防盗控制系统设计

本设计基于51单片机的家用防火防盗控制系统&#xff0c;该系统通过模块间的协同作用实现了对烟雾与天然气浓度的监测、温度监测、人体红外监测、通信传输、声光报警等功能。利用按键模块设置报警的阈值&#xff0c;将处理后的信息与阈值进行对比。判断气体浓度和温度是否超过阈…

酒店智能门锁SDK接口pro[V10] 门锁校验C#-SAAS本地化-未来之窗行业应用跨平台架构

一、代码 int 酒店标识_int Convert.ToInt32(酒店标识);StringBuilder 锁号2024 new StringBuilder(8);//信息 "未知返回值&#xff1a;" bufCard_原始;GetGuestLockNoByCardDataStr_原始(酒店标识_int, bufCard_原始.ToString(), 锁号2024);StringBuilder 退…

C++语言学习(4): identifier 的概念

1. 什么是 identifier identifier 中文意思是标识符&#xff0c;在 cppreference 中明确提到&#xff0c;identifier 是任意长度的数字、下划线、大写字母、小写字母、unicode 字符 的序列&#xff1a; An identifier is an arbitrarily long sequence of digits, underscores…

nginx打包部署前端vue项目全过程【保姆级教程】

&#x1f939;‍♀️潜意识起点&#xff1a;个人主页 &#x1f399;座右铭&#xff1a;得之坦然&#xff0c;失之淡然。 &#x1f48e;擅长领域&#xff1a;前端 是的&#xff0c;我需要您的&#xff1a; &#x1f9e1;点赞❤️关注&#x1f499;收藏&#x1f49b; 是我持…

Python字符串string方法大全及使用方法[1]以及FastAPI框架文件上传的处理-client使用postman

一、Python字符串string方法大全及使用方法[1] 1. Python字符串string方法大全及意义解释 #将字符串的第一个字符转换为大写 capitalize() #返回一个指定的宽度 width 居中的字符串&#xff0c;fillchar 为填充的字符&#xff0c;默认为空格。 center(width, fillchar) #返…

磁编码器磁铁要求和安装要求

总结来说&#xff0c; 磁铁需要是径向两极充磁、牌号N35、直径10mm、高度2.5mm的烧结钕铁硼磁铁。 磁铁的固定套必须是非导磁材料&#xff0c;比如铜、铝、塑料。 磁铁要距离电机轴至少2mm以上。 磁铁距离磁编码芯片0.5~3mm&#xff0c;最好1到2mm。 使用磁编码器的伺服&#…

css设置文本样式属性

目录 1.font-size&#xff1a;字体大小 案例&#xff1a;通过font-size属性设置字体的大小 1.代码 2.效果 2.font-family:字体的展现形式 案例&#xff1a;使用font-family属性设置字体的风格 1.代码实现 2.效果 3. font-weight:字体的粗细 案例&#xff1a;使用font-weight定义…

基于SSM+Vue技术的定制式音乐资讯平台

文未可获取一份本项目的java源码和数据库参考。 一、选题的背景与意义&#xff1a; 随着个人计算机的普及和互联网技术的日渐成熟&#xff0c;网络正逐渐成为人们获取信息及消费的主要渠道。然而在当前这个信息时代&#xff0c;网络中的信息种类和数量呈现爆炸性增长的趋势&a…

爱拼才会赢,甲骨文公司智算中心标配英伟达GPU10万颗

【科技明说 &#xff5c; 科技热点关注】 之前有有外媒消息&#xff0c;甲骨文宣布推出了多款智算集群&#xff0c;可通过甲骨文云基础设施提供AI训练服务&#xff0c;其中最顶级的一款配备了超过10万块的NVIDIA Blackwell GPU。 它一共使用了多达131072万块B200 GPU加速卡&…

单链表及其代码实现

目录 前言单链表1.1 单链表的定义1.2单链表代码实现1.2.1 头文件1.2.2 函数实现文件1.2.3 测试文件1.2.4 野指针问题 总结 前言 本文介绍单链表&#xff0c;主要是创销、增删改查代码实现。 注&#xff1a;文章中函数命名采取STL库。 单链表 1.1 单链表的定义 单链表是链线…

北京市大兴区启动乐享生活 寻味大兴 美食嘉年华 系列促销费活动

北京市大兴区启动乐享生活 寻味大兴 系列促销费活动 区商务局副局长 兰莉 致开幕辞 区餐饮行业协会会长 董志明 介绍活动内容 2024年9月30日&#xff0c;由大兴区商务局主办、大兴区餐饮行业协会承办&#xff0c;并得到高米店街道和大兴绿地缤纷城大力支持的“乐享生活 寻味大…

OceanBase—02(入门篇——对于单副本单节点,由1个observer扩容为3个observer集群)——之前的记录,当初有的问题未解决,目前新版未尝试

OceanBase—02&#xff08;入门篇——对于单副本单节点&#xff0c;由1个observer扩容为3个observer集群&#xff09;——之前的记录&#xff0c;有的问题未解决&#xff0c;新版未尝试 1、前言—安装单副本单节点集群1.1 docker安装OB 2、查看现有集群情况2.1 进入容器&#x…

SOMEIP_ETS_147: SD_Send_triggerEventUINT8_Eventgroup_2

测试目的&#xff1a; 验证DUT在Tester订阅事件组后&#xff0c;能够响应Tester触发的triggerEventUINT8方法&#xff0c;并将TestEventUINT8事件发送到订阅请求中端点选项指定的IP地址和端口。 描述 本测试用例旨在确保DUT能够正确处理事件组的订阅请求&#xff0c;并且在T…

VSOMEIP代码阅读整理(1) - 网卡状态监听

一. 概述 ​ 在routing进程所使用的配置文件中&#xff0c;存在如下配置项目&#xff1a; {"unicast" : "192.168.56.101",..."service-discovery" :{"enable" : "true","multicast" : "224.244.224.245&q…

在2核2G服务器安装部署MySQL数据库可以稳定运行吗?

阿里云2核2G服务器可以安装MySQL数据库吗&#xff1f;当然可以&#xff0c;并且可以稳定运行MySQL数据库&#xff0c;目前阿里云服务器网aliyunfuwuqi.com使用的就是阿里云2核2G服务器&#xff0c;在云服务器上安装MySQL数据库&#xff0c;可以稳定运行。 目前阿腾云用于运行M…

C++系列-继承补充

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 继承和友元 友元关系不能继承&#xff0c;父亲的朋友不能是你的朋友 比如在这个例子当中&#xff1a; class Student; class Person { public:friend void Display(const Per…

厦门网站设计的用户体验优化策略

厦门网站设计的用户体验优化策略 在信息化快速发展的今天&#xff0c;网站作为企业与用户沟通的重要桥梁&#xff0c;用户体验&#xff08;UX&#xff09;的优化显得尤为重要。尤其是在交通便利、旅游资源丰富的厦门&#xff0c;吸引了大量企业进驻。在这样竞争激烈的环境中&am…