Rust 中的引用与借用

目录

1、引用与借用

 1.1 可变引用

1.2 悬垂引用

1.3 引用的规则

2、slice 类型

 2.1 字符串字面量其实就是一个slice

2.2 总结


1、引用与借用

在之前我们将String 类型的值返回给调用函数,这样会导致这个String会被移动到函数中,这样在原来的作用域不可访问了,但是我们功能一个String值得引用,这样就不会导致这个String类型的值被移动,而传递的只是一个引用。引用更像一个指针,因为是一个地址,我们就可以基于这个地址找到改地址上存储的数据。 与指针不同,引用确保指向某个特定类型的有效值。

下面是一个引用传递的示例:

fn main() {
    let str = String::from("hello world!");
    let len = _length(&str);
    println!("str is value: {}", str);
    println!("str length is: {}", len)
} 
fn _length(s: &String) ->usize {
    s.len()
}

 运行结果所示所示:

根据以上代码可以看出_length方法中传递的参数为&str,所以这里传递的是str值的引用,用&符号代表引用

以下是一张对应的示意图:

根据上图也能看出s是s1的引用,引用的是s1在堆中对应类型的值。

注意:与使用 & 引用相反的操作是 解引用dereferencing),它使用解引用运算符,*

变量 s 有效的作用域与函数参数的作用域一样,不过当 s 停止使用时并不丢弃引用指向的数据,因为 s 并没有所有权。当函数使用引用而不是实际值作为参数,无需返回值来交还所有权,因为就不曾拥有所有权。

我们将创建一个引用的行为称为 借用borrowing),因为我们并没有拥有它的所有权,只是暂时借用以下。

我们可以尝试修改一下引用,把引用值改了,看下是否可以,这就类似于我借了别人的东西,然后把东西换了个样子,看看是不是可以呢?

fn main() {
    let str = String::from("hello world!");
    let len = _length(&str);
    println!("str is value: {}", str);
} 
fn _length(s: &String) {
    s.push_str("我把你给改了..........");
}

运行一下,看下结果:

根据提示可以s是一个引用,因此它引用的数据不能作为可变数据借用。

 1.1 可变引用

允许我们修改一个借用的值,这就是 可变引用,把上面的示例改一下,如下所示:

fn main() {
    let mut str = String::from("hello world!");
    _length(&mut str);
    println!("str is value: {}", str);
} 
fn _length(s: &mut String) {
    s.push_str("我把你给改了..........");
}

运行代码,再看一下结果:

 首先定义str必须时可变的,在方法中传递参数,也要指定引用为可变引用,因为引用指向的是被引用的地址,所以就会改变原有的值。

注意:可变引用有一个很大的限制:如果你有一个对该变量的可变引用,你就不能再创建对该变量的引用。

 看以下示例:

fn main() {
    let mut str = String::from("hello world!");
    let a = &mut str;
    let b = &mut str;
    println!("a {}, b{}", a, b)
} 

 根据错误提示,可以知道同一时间不能多次借用str作为可变变量。Rust这样限制是因为可以在编译时就避免数据竞争。数据竞争data race)类似于竞态条件,它可由这三个行为造成:

  • 两个或更多指针同时访问同一数据。
  • 至少有一个指针被用来写入数据。
  • 没有同步数据访问的机制。

再看下以下示例:

fn main() {
    let mut str = String::from("hello world!");
    let c = &mut str;
    let b = &str;
    println!("b{} {}", b, c)
} 

 也会报错,借用和可变借用不能同时被使用。

 再看下一个示例:

fn main() {
    let mut str = String::from("hello world!");
    let c = &mut str;
   
    println!("{}", c);
    let b = &str;
    println!("{}", b)
} 

 可以看到这次是可以打印处结果的,在第一次打印的时候,变量的作用域也就结束了,因而在下次进行赋值时可以的。

1.2 悬垂引用

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。

下面时一个悬垂应用的示例:

fn main() {
    dp();
} 
fn dp() -> &String {    // 返回字符串的引用
    let str = String::from("hello world!");   // 创建一个字符串
    &str      // 返回字符串的引用
}  // str 的作用域结束
// 方法返回的时字符串的引用,而字符串离开作用与,被释放,然在此返回该字符串的引用,
// 就会导致返回的结果不是预期的结果,在Rust中是不让这样操作的。

 直接运行,会报如下错误:

根据报错可知,此函数的返回类型包含借用值,但没有可供借用的值,在返回类型引用处,提示: 错误的声明周期修饰符。

我们改以下返回字符串本身,看一下结果怎么样?

fn main() {
    println!("value is {}", dp())
} 
fn dp() -> String {    
    let str = String::from("hello world!");   
    return str
}  

运行一下看看:

发现对应的值给打印出来,所有权交出去了,所以,可打印出对应的值。

1.3 引用的规则

根据之前的结果,我们可以总结出以下两点:

  • 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
  • 引用必须总是有效的。

2、slice 类型

 slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。slice 是一类引用,所以它没有所有权。

以下有个slice示例:

fn main() {
        let s = String::from("hello world");
        let hello = &s[0..5];
        let world = &s[6..11];
        println!("{}---{}", hello, world)  
} 

运行以下,看下结果如何: 

根据以上结果,可以知道hello变量从字符串(hello world)中进行截取的,开始的位置为0,长度为5,所以打印的结果为hello,而world变量是从开始索引6开始,11结束,11-5=6,那么它的长度也是5,所以打印的结果为world.

Slice 的主要结果包括2部分:

  • 第一部分,是指针,指向数据开始的位置
  • 第二部分,是长度,就是元素结束减去开始位置的值

以下是一个示意图,能够更加清楚知道slice与字符串的关系:

其他写法,例如取前5个字符:

fn main() {
        let s = String::from("hello world");
        let hello = &s[0..5];
        let hello1 = &s[..5];  // hello 和 hello1 是等价的
        println!("{}---{}", hello, hello1)  
} 

例如取最后5个字符:

fn main() {
        let s = String::from("hello world");
        let world = &s[6..];
        let world1 = &s[6..];
        println!("{}---{}", world, world1)  
} 

取整个长度的切片:

fn main() {
        let s = String::from("hello world");
        let world = &s[..];
        let world1 = &s[..];
        println!("{}---{}", world, world1)  
} 

注意:字符串 slice range 的索引必须位于有效的字符边界内,如果尝试从超过边界访问超出索引范围将导致panic错误。

 2.1 字符串字面量其实就是一个slice

一个示例如下所示:

这里 s1 的类型是 &str:world 的类型也是&str,所以s1它是一个指向二进制程序特定位置的 slice。这也就是为什么字符串字面值是不可变的;&str 是一个不可变引用。

2.2 总结

所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。

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

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

相关文章

网络运维Day14

监控概述 监控的目的 报告系统运行状况每一部分必须同时监控内容包括吞吐量、反应时间、使用率等提前发现问题进行服务器性能调整前,知道调整什么找出系统的瓶颈在什么地方 监控的资源类别 公开数据 Web、FTP、SSH、数据库等应用服务TCP或UDP端口 私有数据 CPU、内…

互联网Java工程师面试题·微服务篇·第三弹

目录 34、什么是端到端微服务测试? 35、Container 在微服务中的用途是什么? 36、什么是微服务架构中的 DRY? 37、什么是消费者驱动的合同(CDC)? 38、Web,RESTful API 在微服务中的作用是什…

深度学习 YOLO 实现车牌识别算法 计算机竞赛

文章目录 0 前言1 课题介绍2 算法简介2.1网络架构 3 数据准备4 模型训练5 实现效果5.1 图片识别效果5.2视频识别效果 6 部分关键代码7 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于yolov5的深度学习车牌识别系统实现 该项目较…

《QT从基础到进阶·二十二》QGraphicsView显示大量图形项item导致界面卡顿的解决办法

有时候因业务需要,paint函数在界面上绘制了成百上千个图形项Items,导致操作界面的时候有明显的卡顿感,下文会提供一种比较好的解决办法,先来了解下QGraphicsItem的缓存方式。 (1)setCacheMode(QGraphicsIt…

逐帧动画demo

用这一张图实现一个在跑的猎豹的动画 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X…

20.有效的括号(LeetCode)

思路&#xff1a;用栈的后进先出的特性&#xff0c;来完成题目的要求 因为C有库&#xff0c;可以直接用&#xff0c;而C语言没有&#xff0c;所以我们直接把写好的栈拷贝上来用。 首先&#xff0c;完成框架的搭建 其次&#xff0c;再实现循环内的部分。1.左括号入栈 2.右括…

【计算机网络笔记】IP子网划分与子网掩码

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

vscode launch.json

有时新的服务器进行调试时&#xff0c;需要设置调试的launch.json的结果 然后就可以打开一个launch.json 其内容如下 {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息&#xff0c;请访问: https://go.microsoft.com/fwlink/?linkid83…

Linux socket编程(2):socket函数介绍及C/S模型代码实现

上一节简单介绍了一下套接字、字节序和地址结构体的概念&#xff0c;算是对socket有一个入门的了解。这一节就实现一个客户端-服务端的代码&#xff0c;从这个例子中来学习socket函数的使用。 文章目录 1 客户端/服务端模型2 套接字函数2.1 socket:创建套接字2.2 bind:绑定套接…

【PC】开发者日志:竞技比赛验证系统强化

各位玩家大家好&#xff01;欢迎收看本期开发者日志。 在11月1日发布的第26赛季第2轮更新公告中&#xff0c;我们提到了有关强化比赛验证系统的内容。想必各位玩家一定会对我们加强验证系统的背景和意图感到好奇&#xff0c;为此我们想通过今天这篇反作弊开发者日志来向大家更详…

Python高级语法----使用Python进行模式匹配与元组解包

文章目录 1. 模式匹配的新特性2. 高级元组解包技巧3. 数据类的匹配与应用1. 模式匹配的新特性 Python自3.10版本起引入了结构化模式匹配的新特性,这是一种强大的工具,允许开发者用更清晰、更直观的方式处理数据结构。模式匹配类似于其他编程语言中的switch-case语句,但它更…

C++字典树算法:找出强数对的最大异或值 II

涉及知识点 数学 字典树 题目 给你一个下标从 0 开始的整数数组 nums 。如果一对整数 x 和 y 满足以下条件&#xff0c;则称其为 强数对 &#xff1a; |x - y| < min(x, y) 你需要从 nums 中选出两个整数&#xff0c;且满足&#xff1a;这两个整数可以形成一个强数对&…

Python高级语法----Python C扩展与性能优化

文章目录 1. 编写Python C扩展模块示例代码编译和运行运行结果2. 利用Cython优化性能示例代码编译和运行运行结果3. Python性能分析工具示例代码分析结果1. 编写Python C扩展模块 Python C扩展模块允许你将C语言代码集成到Python程序中,以提高性能。这对于计算密集型任务特别…

20.1 platform 设备驱动

一、Linux 驱动的分离与分层 1. 驱动的分隔和分离 现在有三个平台&#xff0c;A、B 和 C&#xff0c;这三个平台都有 MPU6050 设备。编写最简单的驱动框架如下图&#xff1a; 每个平台下都有一个主机驱动和设备驱动&#xff0c;主机驱动是必要的&#xff0c;因为不同的平台 I2…

ceph修复pg inconsistent( scrub errors)

异常情况 1、收到异常情况如下: OSD_SCRUB_ERRORS 12 scrub errors PG_DAMAGED Possible data damage: 1 pg inconsistentpg 6.d is activeremappedinconsistentbackfill_wait, acting [5,7,4]2、查看详细信息 登录后复制 #ceph health detail HEALTH_ERR 12 scrub errors…

基于 PostgreSQL 构建 AI 电商产品图片相似度搜索方案

在这篇文章中&#xff0c;将介绍如何基于向量数据库&#xff0c;构建一个电商产品图片目录的向量相似度查询解决方案。我们将通过 Amazon SageMaker、pgvector 向量数据库扩展插件、小型语言模型助力 AI 图片搜索能力&#xff0c;从而在产品目录中查找到最符合条件的产品&#…

内网渗透(frp和proxychains4)

一、准备工作 需要三台机器&#xff0c;去哦这里准备的是win7&#xff08;目标主机&#xff09;&#xff0c;kali&#xff08;攻击者&#xff09;&#xff0c;红帽&#xff08;跳板&#xff09; 攻击机&#xff08;kali&#xff09;&#xff1a;192.168.10.15 跳板机&#xff0…

未来智能工厂如何从实时定位系统 (RTLS) 获取价值

实时定位系统数据技术取得了显着进步&#xff0c;制造商如何利用这些数据来优化流程并在其独特的环境中提高价值将推动他们在未来几年取得成功 以下信息源自 WISER 使用 (UWB) RTLS 解决方案在重工业环境中测试和验证的经验&#xff1a; 第 1 章&#xff1a;工业 4.0 中位置数…

桶装水订水送水app有哪些功能?

桶装水订水送水app是一款专为送水工量身打造的提供送水服务的软件&#xff0c;在这里&#xff0c;送水人员将更好的在线发布一些送水信息&#xff0c;在线接单等功能&#xff0c;极大的提高了工作效率&#xff0c;方便了日常生活。 系统的商户端&#xff0c;专为送水工日常送水…
最新文章