【第六课】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中常见的关于引用的用例,切片和迭代器。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1024程序员节:永无bug

引言 每年的10月24日是程序员节。这一天不仅是程序员们的节日,更是对整个行业的庆祝与思考。在这个特殊的日子里,我们不仅回顾过去一年的成就与挑战,也展望未来的发展与机遇。本篇文章将围绕程序员节的主题,探讨前端技术的最新动…

STM32设计学生宿舍监测控制系统-分享

目录 前言 一、本设计主要实现哪些很“开门”功能? 二、电路设计原理图 电路图采用Altium Designer进行设计: 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 本项目旨在利用STM32单片机为核心,结合传感器技术、无线通信技…

Node.js | Yarn下载安装与环境配置

一、安装Node.js Yarn 是 Node.js 下的包管理工具,因此想要使用 Yarn 就必须先下载 Node.js。 推荐参考:Node.js | npm下载安装及环境配置教程 二、Yarn安装 打开cmd,输入以下命令: npm install -g yarn检查是否安装成功&…

【MySQL】MySQL在Centos环境安装

🔥个人主页: Forcible Bug Maker 🔥专栏: MySQL 目录 🌈前言🔥卸载不要的环境🔥检查系统安装包🔥卸载这些默认安装包🔥获取mysql官方yum源🔥安装mysql yum源…

selenium元素定位校验以及遇到的元素操作问题记录

页面元素定位方法及校验 使用比较多的是通过id、class和xpath来对元素进行定位。在定位前可以现在浏览器验证是否可以找到指定的元素。这样就不用每添加一个元素定位都运行代码来检查定位方式表达式是否正确。 使用XPATH定位 在浏览器F12,找到元素,在元…

LLM文档对话 —— pdf解析关键问题

一、为什么需要进行pdf解析? 最近在探索ChatPDF和ChatDoc等方案的思路,也就是用LLM实现文档助手。在此记录一些难题和解决方案,首先讲解主要思想,其次以问题回答的形式展开。 二、为什么需要对pdf进行解析? 当利用L…

小试牛刀-Anchor安装和基础测试

目录 一、编写目的 二、安装步骤 2.1 安装Rust 设置rustup镜像 安装Rust 2.2 安装node.js 2.3 安装Solana-CLI 2.4 安装Anchor CLI 三、Program测试 四、可能出现的问题 Welcome to Code Blocks blog 本篇文章主要介绍了 [Anchor安装和基础测试] 博主广交技术好友&…

Ubuntu 的 ROS 操作系统 turtlebot3 导航仿真

引言 导航仿真是机器人自动化系统中不可或缺的一部分,能够帮助开发者在虚拟环境中测试机器人在复杂场景下的运动与路径规划。 在 Gazebo 仿真环境中,TurtleBot3 配合 ROS 操作系统提供了强大的导航功能。在进行导航仿真时,首先需要准备地图&…

基于Java Springboot网络相册系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…

AI 使用心态大转变:如何让 AI 成为日常工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

项目-摄像

树莓派摄像头使用方法 Camera教程 https://www.raspi.cc/index.php?cread&id53&page1 nanopc-t4 ​https://www.raspi.cc/index.php?cread&id53&page1 摄像头型号 Raspberry Pi Camera Rev 1.3 检测故障 dmesg | grep -i mipi piNanoPC-T4:~$ dmesg | …

基于SSM的农家乐管理系统+论文示例参考

1.项目介绍 功能模块:管理员(农家乐管理、美食信息管理、住宿信息管理、活动信息、用户管理、活动报名、论坛等),普通用户(注册登录、活动报名、客房预订、用户评价、收藏管理、模拟支付等)技术选型&#…

RabbitMQ消息可靠性保证机制4--消费端限流

7.7 消费端限流 在类似如秒杀活动中,一开始会有大量并发写请求到达服务端,城机对消息进行削峰处理,如何做? 当消息投递的速度远快于消费的速度时,随着时间积累就会出现“消息积压”。消息中间件本身是具备一定的缓冲…

Orcad 输出有链接属性的PDF

安装adobe pdf安装Ghostscript修改C:\Cadence\SPB_16.6\tools\capture\tclscripts\capUtils\capPdfUtil.tcl ​ 设置默认打印机为 Adobe PDF ​ 将Ghostscript的路径修改正确 打开cadence Orcad ,accessories->candece Tcl/Tk Utilities-> Utilities->PD…

android:taskAffinity 对Activity退出时跳转的影响

android:taskAffinity 对Activity跳转的影响 概述taskAffinity 的工作机制taskAffinity对 Activity 跳转的影响一个实际的开发问题总结参考 概述 在 Android 开发中,任务栈(Task)是一个核心概念。它决定了应用程序的 Activity 如何相互交互以…

Golang | Leetcode Golang题解之第565题数组嵌套

题目&#xff1a; 题解&#xff1a; func arrayNesting(nums []int) (ans int) {n : len(nums)for i : range nums {cnt : 0for nums[i] < n {i, nums[i] nums[i], ncnt}if cnt > ans {ans cnt}}return }

类和对象——拷贝构造函数,赋值运算符重载(C++)

1.拷⻉构造函数 如果⼀个构造函数的第⼀个参数是自身类类型的引用&#xff0c;且任何额外的参数都有默认值&#xff0c;则此构造函数也叫做拷贝构造函数&#xff0c;也就是说拷贝构造是⼀个特殊的构造函数。 // 拷贝构造函数//d2(d1) Date(const Date& d) {_year d._yea…

基于Java Springboot宿舍管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

Flutter:key的作用原理(LocalKey ,GlobalKey)

第一段代码实现的内容&#xff1a;创建了3个块&#xff0c;随机3个颜色&#xff0c;每次点击按钮时&#xff0c;把第一个块删除 import dart:math; import package:flutter/material.dart; import package:flutter_one/demo.dart;void main() {runApp(const App()); }class App…

爬虫基础总结 —— 附带爬取案例

Crawler —— Learning experience 数据的传输&#xff1a; 在OSI七层模型中&#xff0c;传输层为源主机和目标主机之间提供可靠的数据传输和通信服务&#xff0c;在该层中&#xff0c;有两个重要的协议—— TCP与 UDP协议。 TCP协议&#xff08;传输控制协议&#xff09; …