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.mzph.cn/news/141659.shtml

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

相关文章

Python数据结构: 列表(List)详解

在Python中,列表(List)是一种有序、可变的数据类型,被广泛用于存储和处理多个元素。列表是一种容器,可以包含任意数据类型的元素,包括数字、字符串、列表、字典等。本文将深入讨论列表的各个方面&#xff0…

TCP连接出现大量CLOSE_WAIT不回收的问题排查

背景 日常运维过程中,收到“应用A”突然挂起没有处理请求的告警,然后触发“存活检查”不通过,自动重启了。 问题 为什么“应用A”突然挂起? 分析 排查过程很长,走了很多弯路,这里只列出本案例有效行动…

K8S知识点(八)

(1)实战入门-Label 通过标签实现Pod的区分,说白了就是一种标签选择机制 可以使用命令是否加了标签: 打标签: 更新标签: 筛选标签: 修改配置文件,重新创建一个pod 筛选&#xff1…

vue项目中在scss代码中使用data中的变量

尽管在日常开发中&#xff0c;这类情况实际上很少出现。 VUE2: 在HTML中使用时&#xff0c;请确保将cssVars绑定在需要使用CSS变量的元素或该元素的上层元素上。 <template><div :style"cssVars"><div class"test"/></div><…

Java集合框架

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

jwt工作原理及组成结构

JWT&#xff08;JSON Web Token&#xff09;是一种用于身份验证和授权的开放标准&#xff08;RFC 7519&#xff09;&#xff0c;它是一种安全的、轻量级的身份验证方式。 JWT由三部分组成&#xff1a;头部&#xff08;Header&#xff09;、载荷&#xff08;Payload&#xff09…

代理模式-静态动态代理-jdk动态代理-cglib动态代理

代理模式 静态代理 动态代理&#xff1a;jdk动态代理 cglib动态代理 注意 &#xff1a;下面的代码截图 要配合文字去看 我对代码的每一步都做了解释 所以需要配合图片观看提取吗1111https://pan.baidu.com/s/1OxQSwbQ--t5Zvmwzjh1T0A?pwd1111 这里直接把项目文件 及代码 …

Qt生产者消费者使用QWaitCondition

继承 QThread 重写 void run() Q_DECL_OVERRIDE; 调用start()开启线程 使用 QMutex mutex; QWaitCondition newdataAvailable; 将互斥量锁住 QMutexLocker locker(&mutex); m_stoptrue; 生产者唤醒所有线程表示创建了资源&#xff1a; newdataAvailable.wa…

MySQL(15):存储过程与函数

存储过程概述 含义&#xff1a; 存储过程的英文是 Stored Procedure 。它的思想很简单&#xff0c;就是一组经过 预先编译 的 SQL 语句的封装。 执行过程&#xff1a; 存储过程预先存储在 MySQL 服务器上&#xff0c;需要执行的时候&#xff0c;客户端只需要向服务器端发出调用…

小程序(uniapp)获取位置失败提示用户手机是否开启定位或小程序是否授权位置信息

//地址信息获取 getAddressInfo(){let thatthis;try{ uni.getLocation({ type: gcj02, geocode:true,//设置该参数为true可直接获取经纬度 success: function (resF){console.log(resF) },fail: function (){ //地址获取失败提示用户执行相关操作that.openSetting(); }});}catc…

一文简单聊聊protobuf

目录 基本介绍 原理 同类对比 为什么要使用protobuf? 基本介绍 protobuf的全称是Protocol Buffer&#xff0c;是Google提供的一种数据序列化协议。Protocol Buffers 是一种轻便高效的结构化数据存储格式&#xff0c;可以用于结构化数据序列化&#xff0c;很适合做数据存储…

RFID技术在仓储物流管理中的应用方案

一、方案背景 当前市场竞争日益激烈&#xff0c;提高生产效率、降低运营成本对来说企业至关重要&#xff0c;仓储物流管理在各个行业广泛应用&#xff0c;设计和建立完善的仓储管理流程&#xff0c;提高仓储周转效率&#xff0c;减少运营资金的占用&#xff0c;将冻结的资产转…

【ElasticSearch系列-08】ElasticSearch处理对象间的关联关系

ElasticSearch系列整体栏目 内容链接地址【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631【三】ElasticSearch的高级查询Quer…

github私有仓库开发,公开仓库发布版本

文章目录 github私有仓库开发,公开仓库发布版本需求背景实现思路GitHub Releases具体步骤广告 github私有仓库开发,公开仓库发布版本 需求背景 github私有仓库开发,公开仓库发布版本&#xff0c;既可以保护源代码,又可以发布版本给用户使用。许多知名软件项目都采用了这样的开…

K8s-Traefik Ingress Controller

Traefik Ingress Controller Traefik 是一个为了让部署微服务更加便捷而诞生的现代 HTTP 反向代理、负载均衡工具。traefik 本身设计的就能够实时跟 kubernetes api 交互&#xff0c;感知后端 service&#xff0c;pod 等的变化&#xff0c;自动更新配置并重载。 traefik 是一…

多机器人群体的任务状态与机器人状态同步设计思路

背景技术 近年来&#xff0c;随着科学技术的发展需要&#xff0c;机器人技术不断进步。面临任务的日益复杂化&#xff0c;单机器人在很多环境下已经无法满足生产要求&#xff0c;于是国内外科研工作者对多机器人技术投入了大量关注&#xff0c;提出了利用多机器人协作来代替单机…

拍摄视频的时候相机断电导致视频文件损坏,怎么修复

3-4 现在好多人都有自己的相机&#xff0c;但是专业用来录像的机器应该是不太可能都有的&#xff0c;相机的稳定性会比专业的机器差一些&#xff0c;如果用于比较重要的场景&#xff0c;比如婚庆、会议录像、家庭录像使用等&#xff0c;有较少的概率会出现一些奇怪的情况&…

YOLOv8-Seg改进:卷积变体系列篇 | DCNv3可形变卷积基于DCNv2优化 | CVPR2023

🚀🚀🚀本文改进:DCNv3算子,基于DCNv2算子引入共享投射权重、多组机制和采样点调制,引入到YOLOv8,与C2f结合实现二次创新; 🚀🚀🚀DCNv3 亲测在多个数据集能够实现涨点,同样适用于小目标分割 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐…

建造者模式 ( Builder Pattern )(4)

建造者模式 ( Builder Pattern ) 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象 建造者模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 与工厂模式的区别是&#xff1a;建造者模式更加关注与零件装配…

odoo16前端框架源码阅读——启动、菜单、动作

odoo16前端框架源码阅读——启动、菜单、动作 目录&#xff1a;addons/web/static/src 1、main.js odoo实际上是一个单页应用&#xff0c;从名字看&#xff0c;这是前端的入口文件&#xff0c;文件内容也很简单。 /** odoo-module **/import { startWebClient } from "…