【第六课】Rust所有权系统(二)

目录

前言

借用和引用

借用规则

切片和迭代器

总结


前言

上节课介绍了Rust中的所有权系统,简单回顾一下,rust的内存系统系统,每一块内存都有一个主人,主人对这块内存有着读写和释放的权限,当主人离开作用域之后,该内存被自动释放。内存的主人会转移,当内存中的值是复制语义时,主人不会转移,会复制一份在内存中给新的主人,当内存中的值是移动语义时,主人会转移。在大部分编程中移动语义的值会多一些,比如vector,hashmap,都是我们高频使用的集合,还有String,也是高频使用,我们下面举一个例子来看看使用移动语义的数据时,会有什么不方便的地方,也引出今天的主题:引用和借用。

我们的例子是计算一个字符串的长度,非常常见的功能。

使用一个函数计算字符串的长度,这段代码build后会报错的(报错截图贴在下面),为什么会报错呢?我们一行一行的看一看,首先hello-rust在变量绑定给my_str后,my_str是这一块内存的主人,然后我们使用函数计算了字符串的长度,这里注意了,编译器在这里也给出了详细的解释,我们将my_str传递给函数的时候,input其实转移了所有权,在这里hello-rust的所有权从my_str转移给了input,当函数运行结束,input走出了作用域的时候,由于input是hello-rust的主人,所以触发内存清理,那么问题来了,当我们在println中使用my_str时,因为my_str已经不是任何内存的主人了,所以编译不让通过。那如果我们想要完成这样的功能怎么办呢?这里的核心问题出现在移动语义的值一定会造成所有权转移,即使我们并不希望转移所有权,比如目前的例子中,上一节课中我们使用了字典的例子,我们字典的主人是A,每次字典的交接主人都会变,这是不太好的,为什么别人不能来借用字典呢?主人依然是A,但是别的通过可以借用,当前的例子也是一样,如果函数参数input只是借用my_str,而不是替代my_str,那么就可以在后面继续使用my_str。

fn main() {let my_str = String::from("hello-rust");let len = get_length(my_str);println!("{} len = {}", my_str, len);
}fn get_length(input: String) -> usize {input.len()
}

借用和引用

上面的例子中,我们提到了一个重要的概念,就是借用,就好像同学B借用同学A的字典一样,同学B借同学A的字典,这个动作叫做借用,借到了之后,我们称同学B有字典的引用,这就是借用和引用的概念,默认的借用和引用都是不可变的,意思就是同学B只能翻阅字字典,但是不能在字典上做笔记。如果同学B可以在字典上做笔记,那么这个行为叫做可变借用,拿到的也是可变引用。

我们使用不可变借用来重写一下上面的代码,使其不报错。

先简单介绍一下代码,&my_str是获取不可变引用的语法,函数的形参使用&修饰,此时input获取的是my_str的不可变引用,有读权限。

fn main() {let my_str = String::from("hello-rust");let len = get_length(&my_str);println!("{} len = {}", my_str, len);
}fn get_length(input: &String) -> usize {input.len()
}

再看看可变引用,我们完成一个需求,在字符串后面添加字符串,如果要对原始的字符串执行写操作,就必须使用可变借用获取到可变引用,可变借用使用&mut表示,代码如下。

fn main() {let mut my_str = String::from("hello-rust");add_str(&mut my_str);println!("my_str = {}", my_str);}fn add_str(input: &mut String) {input.push_str("-good")
}

借用规则

首先,明确一下借用和所有权,借用无法获取所有权,借用不会变更主人。

借用其实很像读写锁

不可变引用是读锁,可变引用是写锁,在某一个时刻,要么是n个读锁,要么是1个写锁。

切片和迭代器

切片,slice,是引用的一种使用代表,切片其实就是一段引用,并不会拥有所有权。我们可以对字符串、vector、数组,使用切片获取一段数据的引用。

下面是切片的基本使用语法,使用&变量名[start..end]的方式获取一段数据的引用,start和end分别对应开始和结束位置,左闭右开,包头不包尾。

fn main() {let my_str = String::from("hello-rust");let hello = &my_str[0..5];let my_arr = [1, 2, 3, 4, 5];let sub_arr = &my_arr[0..];let my_vec = vec![1, 2, 3, 4, 5];let sub_vec = &my_vec[0..3];}

可变切片,切片是引用的实际应用,由于引用有可变,所以切片也存在可变切片。

如下代码,首先需要将my_vec声明为mut,因为切片是一种引用,修改切片其实在修改底层的数据,使用&mut获取一个可变的引用,修改数据后,打印my_vec的结果,会发现第一个元素被修改为90了。

fn main() {let mut my_vec = vec![1, 2, 3, 4, 5];let sub_vec = &mut my_vec[0..3];sub_vec[0] = 90;println!("{:?}", my_vec);
}

引用的第二个常见应用就是迭代器。

(1)转移所有权

下面的代码使用into_iter()会将集合中元素的所有权转移到迭代器中,所以println!想再次访问my_vec会报错。

fn main() {let mut my_vec = vec![1, 2, 3, 4, 5];for i in my_vec.into_iter() {println!("i = {}", i);}println!("{:?}", my_vec);
}

(2)不可变引用

使用iter()获取集合的不可变迭代器,遍历之后原集合依然可以访问

fn main() {let mut my_vec = vec![1, 2, 3, 4, 5];for i in my_vec.iter() {println!("i = {}", i);}println!("{:?}", my_vec);}

(3)可变引用

使用iter_mut()方法可以获取集合的可变引用,通过*i可以访问到对应的数据,在rust中&表示创建引用,*表示解引用,访问到对应的值,我们通过*i将原来的值都做了加1操作。

fn main() {let mut my_vec = vec![1, 2, 3, 4, 5];for i in my_vec.iter_mut() {println!("i = {}", i);*i = *i + 1;}println!("{:?}", my_vec);
}

总结

这节课我们讲了rust中的借用和引用,借用是行为,引用是结果,避免了只能所有者才能操作数据导致的所有权转移问题,再次强调下三者的关系:

所有者:对数据有读写和释放内存的权限

不可变引用:对数据有读权限

可变引用:对数据有读写权限

在此基础上,介绍了rust中常见的关于引用的用例,切片和迭代器。

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

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

相关文章

ISUP协议视频平台EasyCVR私有化部署视频平台如何实现RTMP推流将大疆无人机的视频画面回传?

在现代视频监控和流媒体技术领域,EasyCVR视频融合云平台以其卓越的性能和灵活性,成为了跨区域、网络化视频监控综合管理的理想选择。作为TSINGSEE青犀视频“云边端”架构体系中的核心组件,私有化部署视频平台EasyCVR不仅能够实现视频数据的集…

排序算法 -归并排序

文章目录 1. 归并排序(Merge Sort)1.1 简介1.2 归并排序的步骤1.3 归并排序c 语言实现代码说明 1.4 时间复杂度1.5 空间复杂度1.6 动画 1. 归并排序(Merge Sort) 1.1 简介 归并排序(Merge Sort)是一种基于…

unity 一个物体随键盘上下左右旋转和前进的脚本

注意:脚本挂在gamaobject 上面 ,操作对象的目标 this.gameObject 为操作对象 using System.Collections; using System.Collections.Generic; using UnityEngine;public class changePosition : MonoBehaviour {//操作对象的目标 this.gameObject 为操…

视频里的音频怎么提取出来成单独文件?音频提取照着这些方法做

在数字时代,视频与音频的分离与重组已成为日常需求之一。无论是出于制作背景音乐、保存讲座内容,还是编辑播客素材,提取视频中的音频并将其保存为单独文件都显得尤为重要。视频里的音频怎么提取出来成单独文件?本文将详细介绍几种…

React(一)

文章目录 项目地址一、创建第一个react项目二、JSX语法2.1 生成列表2.2 大括号识别JS的表达式2.3 列表循环array2.4 条件判断以及假值显示2.5 复杂条件渲染2.6 事件监听和绑定2.7 使用Fregments返回多个根标签2.8 多条件渲染2.9 导出子组件 三、组件3.1 设置组件3.2 props给子组…

记录一下在原有的接口中增加文件上传☞@RequestPart

首先,咱声明一下: RequestBody和 MultipartFile 不可以 同时使用!!! 因为这两者预期的请求内容类型不同。RequestBody 预期请求的 Content-Type 是 application/json 或 application/xml,而 MultipartFile …

HTTPSOK ---助力阿里云免费 SSL 证书自动续期

目前许多用户面临着 SSL 证书过期续期的难题,尤其是对于阿里云的 免费 SSL 证书,每三个月需要手动申请和更新。为了帮助用户更轻松地管理 SSL 证书,现推出了强大的 HTTPSOK 服务,为用户提供了更便捷的自动续期和管理解决方案。 什…

5G的SUCI、SUPI、5G-GUTI使用场景及关系

使用场景(来源于对23.501、23.502、33.501、23.003的理解) 1、UE初始注册时,根据HN Public Key把SUPI加密成SUCI,并发送初始注册请求 2、AMF转发SUCI给AUSF和UDM进行认证,并获取解密后的SUPI 3、AMF根据SUPI生成一个5G-GUTI,并保…

大数据-226 离线数仓 - Flume 优化配置 自定义拦截器 拦截原理 拦截器实现 Java

点一下关注吧!!!非常感谢!!持续更新!!! Java篇开始了! 目前开始更新 MyBatis,一起深入浅出! 目前已经更新到了: Hadoop&#xff0…

PyAEDT:Ansys Electronics Desktop API 简介

在本文中,我将向您介绍 PyAEDT,这是一个 Python 库,旨在增强您对 Ansys Electronics Desktop 或 AEDT 的体验。PyAEDT 通过直接与 AEDT API 交互来简化脚本编写,从而允许在 Ansys 的电磁、热和机械求解器套件之间无缝集成。通过利…

定时器(QTimer)与随机数生成器(QRandomGenerator)的应用实践——Qt(C++)

一、QTimer与QRandomGenerator (一)QTimer(定时器)[2] QTimer类为定时功能提供了一个高级编程接口。在使用QTimer时,实例化一个QTimer对象并将其timeout()发射信号与合适的信号槽相连接。通过调用QTimer的start()函数…

用redis的zset实现日榜,周榜,月榜

思路&#xff1a; 1.初始化一个月的数据&#xff1a; /*** 初始化一个月数据*/Testpublic void initMonthData(){//计算当前时间小时的keylong hourSystem.currentTimeMillis()/(1000*60*60);for(int i1;i<24*30;i){String key"W_hour"(hour-i);Random random new…

通过shell脚本分析部署nginx网络服务

通过shell脚本分析部署nginx网络服务 1.接收用户部署的服务名称 [rootlocalhost xzy]# vim 1.sh [rootlocalhost xzy]# chmod x 1.sh [rootlocalhost xzy]# ./1.sh2.判断服务是否安装 已安装&#xff1b;自定义网站配置路径为/www&#xff1b;并创建共享目录和网页文件&…

威胁驱动的网络安全方法论

摘要 目前的网络安全风险管理实践很大程度上是由合规性要求驱动的&#xff0c;这使得公司/组织不得不在安全控制和漏洞上投入人力/物力。&#xff08;风险管理涉及多个方面&#xff0c;包括资产、威胁、漏洞和控制&#xff0c;并根据事故发生的可能性及造成的影响进行评估。威…

『VUE』30. 生命周期的介绍(详细图文注释)

目录 生命周期生命周期的8阶段生命周期小例子总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 生命周期 每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤&#xff0c;比如设置好数据侦听&#xff0c;编译模板&#xf…

idea 通过git撤销commit但未push的操作

1、undo commit 适用情况&#xff1a;代码修改完了&#xff0c;已经Commit了&#xff0c;但是还未push&#xff0c;然后发现还有地方需要修改不想提交本次记录了。这时可以进行Undo Commit&#xff0c;修改后再重新Commit。注意&#xff1a;如果已经进行了Push&#xff0c;线上…

【graphics】图形绘制 C++

众所周知&#xff0c;周知所众&#xff0c;图形绘制对于竞赛学僧毫无用处&#xff0c;所以这个文章&#xff0c;专门对相关人员教学&#xff08;成长中的码农、高中僧、大学僧&#xff09;。 他人经验教学参考https://blog.csdn.net/qq_46107892/article/details/133386358?o…

Spring Boot出现java: 错误: 无效的源发行版:16的解决方式

第一步&#xff1a; 修改为SDK的目标字节码版本 第二步&#xff1a;CtrlShiftAltS进入项目结构 第三步&#xff1a;pom.xml文件中 在网上搜索和自己SDK适配的Springboot版本&#xff0c;1.8对应的是2.7.1&#xff08;可以用&#xff09; 修改Java版本为1.8 最后的最后&a…

FPGA 第6讲 简单组合逻辑多路选择器

时间&#xff1a;2024.11.11-11.14 一、学习内容 1.组合逻辑 组合逻辑是VerilgHDL设计中一个重要组成部分。从电路本质上讲&#xff0c;组合逻辑电路的特点是输出信号只是当前时刻输入信号的函数&#xff0c;与其他时刻的输入状态无关&#xff0c;无存储电路&#xff0c;也没…

【C++初阶】C++入门

1、C第一个程序 C是脱胎于C语言的&#xff0c;所以也包含了C语言绝大多数的内容&#xff0c;C兼容C语言绝大多数的语法,在C语言中能实现的程序在C中也是可以执行的&#xff0c;但需要将定义文件代码的后缀改为.cpp 就比如hello world程序 // test.cpp #include<stdio.h&g…