【Rust自学】4.4. 引用与借用

4.4.0 写在正文之前

这一节的内容其实就相当于C++的智能指针移动语义在编译器层面做了一些约束。Rust中引用的写法通过编译器的约束写成了C++中最理想、最规范的指针写法。所以学过C++的人对这一章肯定会非常熟悉。

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

4.4.1. 引用

引用让函数使用某个值而不获得其所有权,声明时在类型前加上&即代表引用。例如String的引用就是&String。如果学过C++的话,C++中的解引用符号是*,Rust中也是一样的。

学了引用之后,就可以把上一篇文章最后的示例代码给简化

这是先前的代码:

fn main(){let s1 = String::from("hello");let (s2, len) = calculate_length(s1);println!("The length of '{}' is {}", s2, len);
}fn calculate_length(s:String) -> (String, uszie) {let length = s.len();(s, length)
}

这是修改后的代码:

fn main(){let s1 = String::from("hello");let length = calculate_length(&s1);println!("The length of '{}' is {}", s1, length);
}fn calculate_length(s:&String) -> usize {s.len()
}

对比两者,后者中数据的指针被传入函数calculate_length供其操作,而数据所有权依然在变量s1上。不需要返回元组,也不需要再声明一个变量s2,更加简洁。

函数calculate_length的参数s实际上是一个指针,指向s所在栈内存位置(不会直接指向堆内存中的数据)。这个指针在走出作用域时,Rust并不会消除其指向的数据(因为s没有所有权),只会弹出栈上所存储的指针信息,也就是释放下图中的最左侧的部分所占的内存。
请添加图片描述

这种以引用作为函数的参数叫做借用

4.4.2. 借用的特性

借用的内容是不能被修改的,除非是可变引用

以房产为例:你把自己有房产权的房子租给别人就是借用,租户只能住不能乱装修,这就是借用的内容不能被修改的特性;如果你允许租客装修,这就是可变引用。

以这个代码为例:

fn main(){let s1 = String::from("hello");let length = calculate_length(&s1);println!("The length of '{}' is {}", s1, length);
}fn calculate_length(s:&String) -> usize {s.push_str(", world");s.len()
}

在编译时这个代码会报错:

error[E0596]: cannot borrow `*s` as mutable, as it is behind a `&` reference

报错的原因在于s.push_str(", world");这一行:引用默认是不可变的,但这一行修改了其数据内容。

引用跟普通的变量声明一样,默认不可变,但加上mut关键字后就可变了:

fn main(){let mut s1 = String::from("hello");let length = calculate_length(&mut s1);println!("The length of '{}' is {}", s1, length);
}fn calculate_length(s:&mut String) -> usize {s.push_str(", world");s.len()
}

这样写就不会报错了(但记得在声明s1时把s1声明为可变变量)

这种可以修改数据内容的引用就叫做可变引用

4.4.3. 可变引用的限制

可变引用有两个非常重要的限制,其一是:在特定作用域内,对某一块数据,只能有一个可变的引用。

以这个代码为例:

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

因为s1s2都是指向s的可变引用,且在同一个作用域内,所以在编译时会报错:

error[E0499]: cannot borrow `s` as mutable more than once at a time

这么做的目的是防止数据竞争,以下三种条件同时满足时会发生数据竞争:

  • 两个或多个指针同时访问同一个数据
  • 至少有一个指针用于写入数据
  • 没有使用任何机制来同步对数据的访问

在报错信息中提及了at a time,意思为同时(也就是在同一个作用域内)。所以说,只要不同时,也就是两个可变引用在不同的作用域指向同一块数据是可以的。下面的代码就体现了这一点:

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

s1s2作用域不相同,所以指向同一块数据是允许的。

可变引用的第二个重要限制是:不可以同时拥有一个可变引用和一个不变的引用 因为可变引用存在的目的是修改数据内容,不变的引用存在的作用就是为了保持数据内容不变,如果两者同时存在,可变引用修改值之后,不可变引用的作用就失效了。

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

因为s1是可变引用,s2是不可变引用,两者出现在同一个作用域指向同一块数据,所以编译器会报错:

error[E0502]: cannot borrow `s` as mutable because it also borrowed as immutable

当然,多个不可变的引用是可以同时出现的

总结:多个读(不可变引用)是可以同时存在的,多个写(可变引用)可以存在但不能同时,多个写和同时读写是不允许的。

4.4.4. 悬空引用(Dangling References)

在使用指针时非常容易引起叫做悬空指针(Dangling Pointer) 的错误,其定义为:一个指针引用了内存中的某个地址,而这块内存可能已经释放并分配给其他人使用了。

如果你引用了某些数据,Rust编译器保证在引用离开作用域前数据不会离开作用域。 这是Rust保证悬空引用永远不会出现的做法。

以这个代码为例:

fn main() {let r = dangle();
}fn dangle() -> &String {let s = String::from("hello");&s
}
  • 创建了一个局部变量 s:
    变量s是一个String,它被分配在栈上,但其底层数据存储在堆上。
  • 返回对s的引用:
    函数最后通过&s返回了s的引用。
  • s 的作用域结束:
    在函数dangle返回后,变量s离开了作用域,根据Rust所有权规则,s的内存被自动释放,&s所指向的内存数据已不再存储s的数据,返回的引用指向的是已经被释放的内存地址,变成了悬空引用(Dangling Pointer)。

Rust的编译器会检查到这一点,在编译时会报错。

4.4.5. 引用的规则

  • 在任何给定的时刻,只能满足下列条件之一:
    • 一个可变的引用
    • 任意数量不可变的引用
  • 引用必须一直有效

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

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

相关文章

Apache Solr RCE(CVE-2017-12629)--vulhub

Apache Solr 远程命令执行漏洞(CVE-2017-12629) Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现。原理大致是文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个…

OpenGL ES 01 渲染一个四边形

项目架构 着色器封装 vertex #version 300 es // 接收顶点数据 layout (location 0) in vec3 aPos; // 位置变量的属性位置值为0 layout (location 1) in vec4 aColors; // 位置变量的属性位置值为1 out vec4 vertexColor; // 为片段着色器指定一个颜色输出void main() {gl…

游戏渠道假量解决方案

某推广公司在推广过程中被查出“短期内点击量激增”“存在同一地址多次访问”“已注册用户重复注册”等数据作弊行为,法院判罚退还服务费200余万元,并赔偿违约金约350万元。 某公司为提升其游戏在应用商店榜单排名,委托某网络公司进行下载、注…

物联网:全面概述、架构、应用、仿真工具、挑战和未来方向

中文论文标题:物联网:全面概述、架构、应用、仿真工具、挑战和未来方向 英文论文标题:Internet of Things: a comprehensive overview, architectures, applications, simulation tools, challenges and future directions 作者信息&#x…

29、基于springboot的网上购物商城系统研发

本课题是根据用户的需要以及网络的优势建立的一个基于Spring Boot的网上购物商城系统,来满足用户网络购物的需求。 本网上购物商城系统应用Java技术,MYSQL数据库存储数据,基于Spring Boot框架开发。在网站的整个开发过程中,首先对…

Spring Boot--06--整合Swagger

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Swagger一、简介官网:https://swagger.io/ Swagger 的优势 二、基本使用1. 导入相关依赖2. 编写配置文件2.1 配置基本信息2.2 配置接口信息2.3 配置分组…

写SQL太麻烦?免费搭建 Text2SQL 应用,智能写 SQL | OceanBase AI 实践

自OceanBase 4.3.3版本推出以来,向量检索的能力受到了很多客户的关注,也纷纷表达希望OB能拓展更多 多模数据库大模型 的AI应用实践。 在上篇文章 👉 OceanBase LLM,免费构建你的专属 AI 助手 ,我们介绍了如何去搭建一…

题海拾贝:力扣 86.分隔链表

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 1、题…

sql server索引优化语句

第一步 建一个测试表 --create table TestUsers --( -- Id int primary key identity(1,1), -- Username varchar(30) not null, -- Password varchar(10) not null, -- CreateDateTime datetime not null --)第二步 插入100w数据 大概1分钟执行时间 ----插入数据…

多智能体/多机器人网络中的图论法

一、引言 1、网络科学至今受到广泛关注的原因&#xff1a; &#xff08;1&#xff09;大量的学科&#xff08;尤其生物及材料科学&#xff09;需要对元素间相互作用在多层级系统中所扮演的角色有更深层次的理解&#xff1b; &#xff08;2&#xff09;科技的发展促进了综合网…

VS Code Copilot 与 Cursor 对比

选手简介 VS Code Copilot&#xff1a;算是“老牌”编程助手了&#xff0c;虽然Copilot在别的编辑器上也有扩展&#xff0c;不过体验最好的还是VS Code&#xff0c;毕竟都是微软家的所以功能集成更好一些&#xff1b;主要提供的是Complete和Chat能力&#xff0c;也就是代码补全…

基于层次化设计方法,设计一个16位二进制全加器

设计思路&#xff1a;先一个半加器和一个或门&#xff0c;每两个半加器构成一个全加器&#xff0c;每四个全加器组成一个4位全加器&#xff0c;再将这4个4位全加器依次相连组成一个16位二进制全加器。 半加器的逻辑表达式&#xff1a;sumAB; coutAB; 半加器的真值表 全加器的逻…

ES搜索原理

ES搜索原理 bg: 搜索的时候&#xff0c;使用模糊查询经常出现搜索不到的情况&#xff0c;不如mysql的like有效。 https://www.bilibili.com/video/BV1yb421J7oX/?spm_id_from333.337.search-card.all.click&vd_source3f917722acc36b0fcca7cca2d21394e2 基础概念 索引&a…

轻松上手:使用 Vercel 部署 HTML 页面教程

&#x1f600; 在学习前端的过程中&#xff0c;部署项目往往是一个令人头疼的问题。然而&#xff0c;Vercel 为我们提供了一个便捷且免费的解决方案。 Vercel 是一个强大的云平台&#xff0c;专门用于前端项目的部署和托管。它不仅支持多种前端框架和静态网站生成器&#xff0…

STL 剖析

STL 六大组件 「STL 六大组件的交互关系」 Container 通过 Allocator 取得数据储存空间Algorithm 通过 Iterator 存取 Container 内容Functor 可以协助 Algorithm 完成不同的策略变化Adapter 可以修饰或套接 Functor、Iterator 配置器(allocator) 配置器&#xff1a;负责空间…

HTTP—03

触发 GET 请求 1&#xff09;直接在浏览器 地址栏 输入 URL&#xff0c;此时构成了一个GET请求 2&#xff09;HTML中的一些特殊标签可能会触发 例如 img,a,link,script... 3&#xff09;通过Form表单触发&#xff08;Form本质也是一个HTML标签&#xff09; 4&#xff0…

C 数组:索引魔杖点化的数据星图阵列

一、数组 1.数组的概念 数组是⼀组相同类型元素的集合&#xff1b;从这个概念中我们就可以发现2个有价值的信息&#xff1a; 数组中存放的是1个或者多个数据&#xff0c;但是数组元素个数不能为0。数组中存放的多个数据&#xff0c;类型是相同的。 2.数组的分类 数组主要分为一…

苹果手机怎么清理空间:拯救你的拥挤手机

在数字生活的海洋中&#xff0c;我们的苹果手机就像一艘小船&#xff0c;载满了照片、应用、视频和各种下载的“宝贝”。随着时间的推移&#xff0c;这艘小船开始变得拥挤&#xff0c;航行速度放缓&#xff0c;甚至有时候直接卡壳。苹果手机怎么清理空间&#xff1f;是时候学会…

Ubuntu上如何部署Nginx?

环境&#xff1a; Unbuntu 22.04 问题描述&#xff1a; Ubuntu上如何部署Nginx&#xff1f; 解决方案&#xff1a; 在Ubuntu上部署Nginx是一个相对简单的过程&#xff0c;以下是详细的步骤指南。我们将涵盖安装Nginx、启动服务、配置防火墙以及验证安装是否成功。 1. 更新…

javaScriptBOM

1.1、BOM概述 1.1.1、BOM简介 BOM&#xff08;browser Object&#xff09;即浏览器对象模型&#xff0c;它提供了独立于内容而与浏览器窗口进行交互的对象&#xff0c;其核心对象是window。 BOM由一系列的对象构成&#xff0c;并且每个对象都提供了很多方法与属性 BOM缺乏标准…