Rust语言入门教程(八) - 引用与借用

news2024/11/28 0:33:08

上一章的内容中我们讨论了Rust的所有权系统,当我们不想移动值的所有权时,我们可以使用引用和借用,而这正是本章想要讨论的问题。

引用(References)

引用允许你访问或修改数据而无需获取数据的所有权。在 Rust 中,有两种类型的引用:

  • 不可变引用(&T):
    1. 它允许你读取数据,但不能修改它。
    2. 你可以同时拥有多个不可变引用。
    3. 当存在不可变引用时,不能再有可变引用。
  • 可变引用(&mut T):
    1. 它允许你读取和修改数据。
    2. 同一时间,只能有一个可变引用指向同一数据。
    3. 当存在可变引用时,不能再有其他可变或不可变引用,为了防止写竞争或读取数据时数据改变。

请添加图片描述
如上图所示, 在多个线程中如果都存在对同一个值的引用时, 在没有锁的情况下, 对同一个变量进行多个可变引用显然是不安全的。

借用(Borrowing)

借用是指使用引用来访问数据的行为。当你创建一个引用时,实际上是在“借用”值。借用规则如下:

  • 数据的所有者不变:当你借用一个值时,该值的所有权不会转移。
  • 作用域和生命周期:引用必须在其所指向的数据的作用域内有效。这就是 Rust 中的生命周期(lifetime)概念。

引用使用示例

let s1 = String::from("abc");
do_stuff(&s1);
println!("{}", s1);

fn do_stuff(s: &String) {
	// do stuff
}

上面的代码中, 我们还是创建了一个do_stuff()函数, 与上一章不同的是, 我们这个函数接受的参数是一个字符串的引用, &String前面的&符号,表示这是一个String类型的引用, 因此,当调用该函数时,我们传递给他的是对s1的引用, 而s1依然保留了对它的值的所有权。do_stuff()借用了一个对值的引用,它将引用而不是值移动到了函数中, 在函数的作用域结束时, 引用被销毁,我们的借用行为也在此时结束。在函数执行完以后,我们还可以正常的使用s1, 因为其值的所有权没有被移动过。

从内存的视角出发, 当创建一个指向s1的引用时, Rust会创建一个指向s1的指针。 但在Rust中,我们几乎从来不会谈论指针的行为,因为Rust语言在很大程度上会自动处理他们的创建和销毁,Rust中的有一个生命周期的概念,就是用来确保指针始终有效。
请添加图片描述
Rust的编译器可以保证, 不会让用户创建一个引用的存活时间超过它引用的源数据的存活时间,并且指针永远不能指向Null。

可变与不可变引用

引用与变量一样, 默认是不可变的,这意味着,即使被引用的值是可变的,也不能直接通过引用来修改它,例如下面这段代码:

let mut s1 = String::from("abc");
do_stuff(&s1);

fn do_stuff(s: &String) {
	s.insert_str(0, "Hi, "); // Error
}

虽然s1被声明为可变的, 但是它的引用默认是不可变的,因此在函数中试图修改s1的值,编译器就会报错。想要通过引用修改被引用的值,可以将引用声明为可变引用:

let mut s1 = String::from("abc");
do_stuff(&mut s1);

fn do_stuff(s: &mut String) {
	s.insert_str(0, "Hi, ");
}

注意可变引用的格式有些许特别: &mut 变量/类型

解引用

注意到了吗,在上面的例子中的函数中,修改s的值的时候,并没有对其先解引用。我们直接使用了.操作符来访问可变引用上的字符串函数,就像我们在对被引用的值本身进行操作一样。

在Rust中,函数或字段的.运算符会自动对引用进行解引用,获取到最终被引用的值。这样的话,在我们使用.运算符的时候,就不用费劲搞清楚某个变量到底是一个值,还是一个引用,甚至是一个引用的引用了。

那怎么样可以手动的进行解引用的操作呢,和大多数语言一样,在前面加上一个*即可, 解引用操作符的优先级很低,因此需要加上( )

let mut s1 = String::from("abc");
do_stuff(&mut s1);

fn do_stuff(s: &mut String) {
	(*s).insert_str(0, "Hi, ");
}

注意上面说的是在.运算符的情况下, Rust会进行自动的解引用, 对于大多数的其他运算符,例如赋值运算,是需要手动进行解引用的:

let mut s1 = String::from("abc");
do_stuff(&mut s1);

fn do_stuff(s: &mut String) {
	s.insert_str(0, "Hi, ");
	*s = String::from("Replacement");
}

小结

本章介绍了引用和借用的含义,以及使用方法,让我们来简单回顾一下:

x 			// 变量
&x 			//变量的不可变引用
&mut x 		// 变量的可变引用

i32			// 类型
&i32 		// 该类型的不可变引用的类型
&mut i32 	// 该类型的可变引用的类型

x: &mut i32 // x是某个值的可变引用
*x			// *x解引用获取到被引用的值, 可变

x: &i32		// x是某个值的不可变引用
*x			// 解引用获取到被引用的值,不可变

下一章将开始介绍Rust中的结构体。

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

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

相关文章

Camtasia Studio2024专业的屏幕录制和视频剪辑软件

Camtasia2024专业的屏幕录制和视频剪辑软件3000多万专业人士在全球范围内使用Camtasia展示产品,教授课程,培训他人,以更快的速度和更吸引人的方式进行沟通和屏幕分享。使您在Windows和Mac上进行录屏和剪辑创作专业外观的视频变得更为简单。 …

【刷题笔记】加油站||符合思维方式

加油站 文章目录 加油站1 题目描述2 思路3 解题方法 1 题目描述 https://leetcode.cn/problems/gas-station/ 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消…

Blender 连续 5 天遭受大规模 DDoS 攻击

Blender 发布公告指出,在2023年11月18日至23日期间,blender.org 网站遭受了持续的分布式拒绝服务(DDoS)攻击,攻击者通过不断发送请求导致服务器超载,使网站运营严重中断。此次攻击涉及数百个 IP 地址的僵尸…

高并发系统:它的通用设计方法是什么?

Java全能学习面试指南:https://javaxiaobear.cn 我们知道,高并发代表着大流量,高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案,从而抵抗巨大流量的冲击,带给用户更好的使用体验。这些方案好似能…

电子学会C/C++编程等级考试2021年09月(三级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:余数相同问题 已知三个正整数 a,b,c。 现有一个大于1的整数x,将其作为除数分别除a,b,c,得到的余数相同。 请问满足上述条件的x的最小值是多少? 数据保证x有解。输入: 一行,三个不大于1000000的正整数a,b,c,两个整数…

ChatGPT初体验:注册、API Key获取与ChatAPI调用详解

自从2022年10月,ChatGPT诞生以后,实际上已经改变了很多!其火爆程度简直超乎想象,一周的时间用户过百万,两个月的时间用户过亿。 目前ChatGPT4已经把2023年4月以前的人类的知识都学习到了,在软件工程里面&am…

因子分析例题(多元统计分析期末复习)

例一 设某客观现象可用 X {X} X( X 1 {X_1} X1​, X 2 {X_2} X2​, X 3 {X_3} X3​)’ 来描述,在因子分析时,从约相关阵出发计算特征值为 λ 1 {λ_1} λ1​1.754, λ 2 {λ_2} λ2​1, λ 3 {λ_3} λ3​…

3.数据结构

3.1 数据结构分类 常见的数据结构包括数组、链表、栈、队列、哈希表、树、堆、图,它们可以从“逻辑结构”和“物理结构”两个维度进行分类。 3.1.1逻辑结构:线性与非线性 逻辑结构揭示了数据元素之间的逻辑关系。在数组和链表中,数据按照…

【docker系列】docker命令篇

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

C语言:有一篇文章,共三行文字,每行有80个字符。要求分别统计出单词个数、空格数。

分析&#xff1a; #include<stdio.h>&#xff1a;这是一个预处理指令&#xff0c;将stdio.h头文件包含到程序中&#xff0c;以便使用输入输出函数。 int main()&#xff1a;这是程序的主函数&#xff0c;是程序执行的入口点。 char a[3][80];&#xff1a;定义了一个二维…

springBoot的实现原理;SpringBoot是什么;使用SpringBoot的核心功能;springBoot核心注解以及核心配置文件

文章目录 springBootspringBoot的实现原理什么是 Spring Boot&#xff1f;SpringBoot是什么为什么要使用springBootSpring Boot的核心功能Spring Boot 主要有如下优点&#xff1a; SpringBoot启动过程-流程Spring Boot 的核心注解是哪个&#xff1f;什么是 JavaConfig&#xff…

7 通用数字量输入输出GPIO

文章目录 7.0 GPIO概念7.1 GPIO工作原理7.2 GPIO寄存器以及编程7.2.5 GPIO寄存器编程设置与应用 7.3 GPIO跑马灯7.3.1 LED 输出初始化7.3.2 跑马灯输出实验7.3.3 按键输入实验 7.0 GPIO概念 GPIO&#xff08;general purpose intput output&#xff09;是通用输入输出端口的简…

Elasticsearch:LangChain 是什么?

当你将应用程序称为 “AI&#xff08;人工智能&#xff09;” 时&#xff0c;这通常意味着它包含与学习模型&#xff08;例如大型语言模型&#xff0c;或 LLM&#xff09;的交互。 [不那么]有趣的事实是&#xff0c;LLM 的使用实际上并不是使应用程序变得智能的原因。 它的特殊…

vue项目npm install报错Failed at the fibersa4.0.3 install script

报错如下 解决&#xff1a;降低node版本 降到12.16.0 参考链接

基于51单片机的全自动洗衣机proteus仿真设计

标题目录 &#x1f4ab;51单片机全自动洗衣机proteus仿真设计&#x1f4ab;设计介绍&#x1f4ab;仿真图电动机驱动模块电路设计电源模块电路设计控制按键进水阀和排水阀控制继电器 &#x1f4ab;程序设计main函数 &#x1f4ab;设计报告&#x1f4ab;资料清单&&下载链…

【古诗生成AI实战】之三——任务加载器与预处理器

本章内容属于数据处理阶段&#xff0c;将分别介绍任务加载器task和预处理器processor。 [1] 数据集 在深入探讨数据处理的具体步骤之前&#xff0c;让我们先了解一下我们将要使用的数据集的形式。 本项目采用的是七绝数据集&#xff0c;总计83072条古诗&#xff0c;其形式如下&…

Linux系统编写C语言程序并执行(图文详解)

目录 前言 Linux如何编写C语言 1.打开编辑器&#xff0c;写代码 2.编译运行文件 相关拓展 前言 LINUX中包含了很多软件开发工具。它们中的很多是用于C和C应用程序开发的。 C是一种能在UNIX的早期就被广泛使用的通用编程语言。它最早是由Bell实验室的Dennis Ritchie为了UN…

中年人怎么发展?持续发展?

现在ai这么火&#xff0c;就像当年的xxx&#xff0c;如果没有抓住&#xff0c;会xxx吗&#xff1f; 为了ai&#xff0c;多学学python也是也是好的啊。 在学习之余&#xff0c;还是想做做自媒体的。不求马上赚到钱。我的想法是&#xff0c;现在每天下班回家都是刷刷抖音&#…

Jmeter工具学习三——CSV文件、关联、断言

Jmeter学习三——CSV文件和关联 jmeter做功能测试和做性能测试的区别CSV数据文件设置&#xff08;读取外部文件&#xff0c;进行分数据驱动&#xff09;文件设置字段介绍&#xff1a;文件名文件编码如果出现编码问题导致的乱码&#xff0c;如何解决&#xff1f; 变量名忽略首行…

基于深度学习的图像超分辨率应用

引言 在使用图片浏览软件显示图片时&#xff0c;为了凸显某个部位&#xff0c;你会放大图片&#xff0c;为了显示整体&#xff0c;你会缩小图片。 你的原始图片大小是固定的&#xff0c;但图像浏览软件既可以最大化到整个屏幕&#xff0c;也可以只占一个区域&#xff0c;这些…