Rust - 可变引用和悬垂引用

 可变引用

在上一篇文章中,我们提到了借用的概念,将获取引用作为函数参数称为 借用borrowing),通常情况下,我们无法修改借来的变量,但是可以通过可变引用实现修改借来的变量。代码示例如下:

fn main() {let mut s = String::from("hello");  // s是可变的变量change(&mut s);  // &mut 表示可变引用
}fn change(some_string: &mut String) {  // &mut 表示可变引用some_string.push_str(", world");
}

要想实现修改借来的变量就必须将 s改为 mut。然后必须创建一个可变引用 &mut s和接受一个可变引用 some_string: &mut String

但是可变引用有一个很大的限制:在特定作用域中的特定数据只能有一个可变引用。比如下述代码就不会被成功编译。

fn main() {let mut s = String::from("hello"); let r1 = &mut s;let r2 = &mut s;
}

编译运行就会抛出如下异常:

error[E0499]: cannot borrow `s` as mutable more than once at a time--> src/main.rs:5:14|
4 |     let r1 = &mut s;|              ------ first mutable borrow occurs here
5 |     let r2 = &mut s;|              ^^^^^^ second mutable borrow occurs here
6 |
7 |     println!("{}, {}", r1, r2);|                        -- first borrow later used here

所以这种修改借来的变量的可变引用是以一种受限制的方式允许修改,这个限制的好处是 Rust 可以在编译时就避免数据竞争。数据竞争data race)类似于竞态条件,它可由这三个行为造成:

  • 两个或更多指针同时访问同一数据。
  • 至少有一个指针被用来写入数据。
  • 没有同步数据访问的机制。

数据竞争会导致未定义行为,难以在运行时追踪,并且难以诊断和修复;Rust 避免了这种情况的发生。我们可以使用{}创建一个新的作用域,这样就能够允许多个可变引用了,只是不能在同一个作用域中同时拥有:

fn main() {let mut s = String::from("hello");{let r1 = &mut s;} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用let r2 = &mut s;
}

另外还需要注意的是,不能在拥有不可变引用的同时拥有可变引用。不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!但是多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。如下述代码:

fn main() {let mut s = String::from("hello");let r1 = &s; // 没问题let r2 = &s; // 没问题let r3 = &mut s; // 在拥有不可变引用的同时拥有可变引用println!("{}, {}, and {}", r1, r2, r3);} 

上面代码示例编译时会抛出如下异常:

error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable--> src/main.rs:6:14|
4 |     let r1 = &s; // no problem|              -- immutable borrow occurs here
5 |     let r2 = &s; // no problem
6 |     let r3 = &mut s; // BIG PROBLEM|              ^^^^^^ mutable borrow occurs here
7 |
8 |     println!("{}, {}, and {}", r1, r2, r3);|                                -- immutable borrow later used here

但是如果可变引用和不可变引用他们的作用域不重叠代码就是可以编译的,我们可以将上面的代码示例进行修改就可以正常运行了。

fn main() {let mut s = String::from("hello");let r1 = &s; // 没问题let r2 = &s; // 没问题println!("{} and {}", r1, r2);// 此位置之后 r1 和 r2 不再使用let r3 = &mut s; // 没问题println!("{}", r3);}

悬垂引用(Dangling References

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。

当我们不小心创建了悬垂引用,Rust在编译的时候就会抛出异常:

fn main() {let reference_to_nothing = dangle();
}fn dangle() -> &String {  // dangle 返回一个字符串的引用let s = String::from("hello");  // s 是一个新字符串&s  // 返回字符串 s 的引用
}// 这里 s 离开作用域并被丢弃。其内存被释放。

因为 s是在 dangle函数内创建的,当 dangle的代码执行完毕后,s将被释放。不过我们尝试返回它的引用。这意味着这个引用会指向一个无效的 String,所以在编译时Rust就会抛出异常,解决方式就是直接返回String

fn no_dangle() -> String {let s = String::from("hello");s
}  // 所有权被移动出去,内存没有被释放

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

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

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

相关文章

人口问题1

2023年新生儿数量公布,902万人。跟去年比有所下降,但没有各路预测的降那么多。 搜了下历年新生儿数量,建国以来很快上升到2000w,然后遇到了人民公社、三年灾害有些下滑;逐渐克服困难,克服错误,…

SAP S/4HANA 2023 Fully-Activated Appliance 虚拟机版介绍

注:市面上所有在售虚拟机均为拷贝本人所作的虚拟机,存在各种技术问题,请知悉。 SAP S4HANA 2023 FAA版本内置了四个Client: 1、000:SAP初始Client,原则上不能动; 2、100:只激活了US…

NG+WAF实现应用安全访问

一、基本概念 什么是waf? Web应用防火墙(waf)是通过执行一系列针对HTTP/HTTPS的安全策略来专门为Web应用提供保护的一款产品,WAF是一种工作在应用层的、通过特定的安全策略来专门为Web应用提供安全防护的产品。 什么是ngx_lua_…

SpringMVC数据校验

导包 配置springmvc.xml <bean id"validator" class" org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><property name"providerClass" value"org.hibernate.validator.HibernateValidator ">…

[AIGC] 深入理解Java并发编程:从入门到进阶

深入理解Java并发编程&#xff1a;从入门到进阶 引言 在计算机领域中&#xff0c;针对多核处理器的高并发需求&#xff0c;Java并发编程成为了一项重要的技能。Java提供了丰富的并发编程工具和API&#xff0c;使得开发者能够有效地利用多核处理器的优势。本文将介绍Java并发编…

App 设计工具中的回调

目录 创建回调函数 回调函数编程 回调输入参数 在回调函数之间共享数据 在多个组件之间共享回调 以编程方式创建和分配回调 在代码中搜索回调 更改回调或断开与回调的连接 删除回调 示例&#xff1a;具有滑块回调的 App 回调是在用户与 App 中的 UI 组件交互时执行的…

5G_系统同步机制(八)

BBU和RRU的同步机制 为什么要做到系统同步 在TDD模式下工作时&#xff0c;为了避免相邻小区之间的干扰&#xff0c;近距离的所有gNB在任何时间点都必须具有相同的传输方向(DL或UL)。这样做的必要条件是在BTS之间同步SFN (System Frame number)和time Slot。此外&#xff0c;由…

SystemC学习笔记(三) - 查看模块的波形

简述 波形在Simulation/Emulation中地位十分重要&#xff0c;尤其是在研发初期&#xff0c;只能通过波形来查看软件hang住的位置。 对于TLM来说&#xff0c;查看波形一般是指查看pvbus上的transaction&#xff0c;而对于SystemC本身来说&#xff0c;查看波形就是使用Gtkwave或…

QT 浏览器组件使用

1. Qt4中使用webkit模块&#xff1b; 2. Qt5 ~Qt5.5使用webkitwidgets模块,使用WebKit作为后端&#xff1b; 3. Qt5.6以上版本使用webenginewidgets模块,使用Chromium作为后端。QT5.6之后开始支持 验证一下webenginewidgets 64位能不能在win7 64位上运行

决策树的分类

概念 决策树是一种树形结构 树中每个内部节点表示一个特征上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff0c;每个叶子节点代表一种分类结果 决策树的建立过程 1.特征选择&#xff1a;选取有较强分类能力的特征。 2.决策树生成&#xff1a;根据选择的特征生…

SQL的五大约束作用、语法、应用场景及举例

SQL的五大约束包括主键约束&#xff08;PRIMARY KEY&#xff09;、唯一性约束&#xff08;UNIQUE&#xff09;、外键约束&#xff08;FOREIGN KEY&#xff09;、非空约束&#xff08;NOT NULL&#xff09;和默认约束&#xff08;DEFAULT&#xff09;。 sql约束的作用&#xff1…

《ORANGE’S:一个操作系统的实现》读书笔记(三十五)内存管理(三)

内存管理到目前为止实现了fork()、exit()和wait()&#xff0c;而我们的目标是实现一个可以执行命令的shell&#xff0c;可无论Init进程fork出多少进程&#xff0c;它也都只是Init而已。所以我们还需要一个系统调用&#xff0c;它就是exec()&#xff0c;这篇文章就来记录exec()的…

最长公共前缀(Leetcode14)

例题&#xff1a; 分析&#xff1a; 我们可以先定义两个变量 i &#xff0c; j&#xff0c; j表示数组中的每一个字符串&#xff0c; i 表示每个字符串中的第几个字符。一列一列地进行比较&#xff0c;先比较第一列的字符&#xff0c;若都相同&#xff0c;则 i &#xff0c;继…

MySQL存储过程和触发器

存储过程和触发器是数据库中用于执行特定任务的两种重要的数据库对象。它们由SQL语句组成&#xff0c;被预先编译并存储在数据库中&#xff0c;可以通过调用来执行。以下是对存储过程和触发器的简要说明以及如何创建和使用它们&#xff1a; 存储过程&#xff08;Stored Proced…

成功解决「MySQL问题1」启动mysql时:发生系统错误5拒绝访问

我 | 在这里 &#x1f575;️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 &#x1f3e0; 工作 | 广州 ⭐ Java 全栈开发&#xff08;软件工程师&#xff09; ✈️公众号 | 热爱技术的小郑 。文章底部有个人公众号二维码。回复 Java全套视频教程 或 前端全套视频教程 即可获取 300G 教程资…

【数据结构】 顺序表的基本操作 (C语言版)

一、顺序表 1、顺序表的定义&#xff1a; 线性表的顺序存储结构&#xff0c;即将表中的结点按逻辑顺序依次存放在一组地址连续的存储单元里。这种存储方式使得在逻辑结构上相邻的数据元素在物理存储上也是相邻的&#xff0c;可以通过数据元素的物理存储位置来反映其逻辑关系。…

漫漫数学之旅009

文章目录 经典格言数学习题古今评注拓展学习&#xff08;一&#xff09;大数定理&#xff08;二&#xff09;伯努利级数 经典格言 真正的问题&#xff0c;不在于机器是否思考&#xff0c;而在于人们是否思考。——BF斯金纳&#xff08;B. F. Skinner&#xff09; BF斯金纳&…

ubuntu双屏扩展

nvidia 驱动安装 安装依赖 sudo apt-get update #更新软件列表 sudo apt-get install g gcc make build-essential libc6-dev禁用nouveau sudo vim /etc/modprobe.d/blacklist.conf #在最后添加如下两行&#xff1a;blacklist nouveauoptions nouveau modeset0 #然后更新…

MVC和MVVM区别和VUE关系

MVC&#xff08;Model-View-Controller&#xff09;和 MVVM&#xff08;Model-View-ViewModel&#xff09;是两种常见的前端架构模式&#xff0c;它们的主要区别在于处理业务逻辑和数据操作的方式。 MVC中&#xff0c;View&#xff08;视图&#xff09;可以直接访问Model&…

低代码+物联网: 重塑智慧社区,开启未来生活新纪元

在数字化时代的推动下&#xff0c;人们对于智能化生活的需求不断增长。而作为连接物理世界和数字世界的重要桥梁&#xff0c;物联网技术发挥着至关重要的作用。最近几年&#xff0c;低代码开发平台的兴起更是给物联网应用开发带来了革命性的改变。本文将探讨低代码和物联网的结…