【第六课】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不仅能够实现视频数据的集…

LeetCode 2816.翻倍以链表形式表示的数字

题目: 给你一个 非空 链表的头节点 head ,表示一个不含前导零的非负数整数。 将链表 翻倍 后,返回头节点 head 。 思路: 思路一:反转链表,两个相同的链表求和 思路二:如果不考虑进位&#…

力扣(leetcode)面试经典150题——27. 移除元素

题目 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作&#xff1a…

VueDPlayer视频插件的使用

VueDPlayer 是一个基于 DPlayer 的 Vue 封装组件,DPlayer 是一个 HTML5 视频播放器,支持弹幕、视频倍速播放、视频预加载等功能。 以下是如何在 Vue.js 项目中使用 VueDPlayer 的步骤: 1.安装 VueDPlayer: 使用 npm 或 yarn 安装…

排序算法 -归并排序

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

前端开发之 节流与防抖

防抖节流的作用是什么? 节流(throttle)与 防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。 其实很多前…

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

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

C# 事件编程详解

文章目录 1.什么是事件?2.事件的声明与使用2.1 声明事件2.2 订阅与触发事件3.事件的核心概念3.1 事件处理委托3.2 自定义事件参数4.事件的高级用法4.1 多播委托与事件4.2 事件解除订阅4.3 自定义事件访问器5.事件的应用场景5.1 GUI 应用程序中的事件5.2 基于事件的编程模型5.3…

C# 属性与结构

C# 属性 C# 属性,属性是一种特殊的类成员。 我们使用预定义的 set 和 get 方法来访问和修改它们。 属性读取和写入会转换为获取和设置方法调用。 与使用自定义方法调用(例如object.GetName())相比,使用字段符号(例如o…

Linux系统性能调优技巧详解

Linux系统性能调优技巧详解 在Linux系统中,性能调优是确保系统在高负载下依然能够稳定、高效运行的重要环节。调优的目标包括优化系统资源的利用率(如CPU、内存、磁盘和网络),减少瓶颈,并提升系统的响应速度。本文将深入探讨Linux系统性能调优的技巧,并结合代码使用案例…

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

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

基于Canny边缘检测和轮廓检测

这段代码实现了基于Canny边缘检测和轮廓检测,从图像中筛选出面积较大的矩形,并使用OpenCV和Matplotlib显示结果。主要流程如下: 步骤详解: 读取图像: img cv2.imread(U:/1.png)使用cv2.imread()加载图像。 转换为灰…

cisco防火墙在内网通过外网域名进行访问的配置

1.配置主机的access-list列表 access-list outside_acl extended permit tcp any 192.168.1.123 2.对主机和端口进行映射, 2.1 nat (inside,outside) source static 192.168.1.123 interface service stcp80 stcp8800 注释:先对主机进行外网映射…

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,并保…

【微服务】Spring AI 使用详解

目录 一、前言 二、Spring AI 概述 2.1 什么是Spring AI 2.2 Spring AI 特点 2.3 Spring AI 带来的便利 2.4 Spring AI 应用领域 2.4.1 聊天模型 2.4.2 文本到图像模型 2.4.3 音频转文本 2.4.4 嵌入大模型使用 2.4.5 矢量数据库支持 2.4.6 数据工程ETL框架 三、Sp…

【jvm】方法区的理解

目录 1. 说明2. 方法区的演进3. 内部结构4. 作用5.内存管理 1. 说明 1.方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。它是各个线程共享的内存区域。2.尽管《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分,但它却…

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

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