Rust编程基础核心之所有权(下)

1.变量与数据交互方式之二: 克隆

在上一节中, 我们讨论了变量与数据交互的第一种方式: 移动, 本节将介绍第二种方式:克隆。

如果我们 确实 需要深度复制 String 中堆上的数据,而不仅仅是栈上的数据,可以使用一个叫做 clone 的通用函数。

看下面的代码:

let s1 = String::from("hello");
let s2 = s1.clone();
​
println!("s1 = {}, s2 = {}", s1, s2);

这段代码能正常运行, 并且堆上的数据现在可以被复制了。

我们在代码中下个断点, 使用调试器观察下s1的内容,如图:

结合上一章节的分析, 此时s1变量中保存的指针ptr指向的内存保存了内容:"hello"。

现在执行语句: let s2 = s1.clone(); 单不执行一下看下s2的内容,如图:

可以看到, clone()函数的确将字符串内容复制到变量s2中。

注意:当出现 clone 调用时,我们心里要清楚一些特定的代码被执行而且这些代码可能相当消耗资源。很容易能察觉到一些不寻常的事情正在发生。

下面再看一段代码:

let x = 5;
let y = x;
​
println!("x = {}, y = {}", x, y);

执行这段代码, 结果如下:

这段代码似乎与我们刚刚学到的内容相矛盾:没有调用 clone,不过 x 依然有效且没有被移动到 y 中。

原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 y 后使 x 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 clone 并不会与通常的浅拷贝有什么不同,我们可以不用管它。

Rust 有一个叫做 Copy trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上, 如果一个类型实现了 Copy trait,那么一个旧的变量在将其赋值给其他变量后仍然可用。

Rust 不允许自身或其任何部分实现了 Drop trait 的类型使用 Copy trait。如果我们对其值离开作用域时需要特殊处理的类型使用 Copy 注解,将会出现一个编译时错误。

那么哪些类型实现了 Copy trait 呢?可以查看给定类型的文档来确认,不过作为一个通用的规则,任何一组简单标量值的组合都可以实现 Copy,任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:

  • 所有整数类型,比如 u32

  • 布尔类型,bool,它的值是 truefalse

  • 所有浮点数类型,比如 f64

  • 字符类型,char

  • 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。

2.所有权和函数

将值传递给函数与给变量赋值的原理相似。向函数传递值可能会移动或者复制,就像赋值语句一样。

看一下下面的代码:

fn main() {let s = String::from("hello");  // s 进入作用域
​takes_ownership(s);             // s 的值移动到函数里 ...// ... 所以到这里不再有效
​let x = 5;                      // x 进入作用域
​makes_copy(x);                  // x 应该移动函数里,// 但 i32 是 Copy 的,// 所以在后面可继续使用 x
​
} // 这里,x 先移出了作用域,然后是 s。但因为 s 的值已被移走,// 没有特殊之处
​
fn takes_ownership(some_string: String) { // some_string 进入作用域println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。// 占用的内存被释放
​
fn makes_copy(some_integer: i32) { // some_integer 进入作用域println!("{}", some_integer);
} // 这里,some_integer 移出作用域。没有特殊之处

当尝试在调用 takes_ownership 后使用 s 时,Rust 会抛出一个编译时错误。这些静态检查使我们免于犯错。

3.返回值与作用域

返回值也可以转移所有权, 看下面的代码:

fn main() {let s1 = gives_ownership();         // gives_ownership 将返回值// 转移给 s1
​let s2 = String::from("hello");     // s2 进入作用域
​let s3 = takes_and_gives_back(s2);  // s2 被移动到// takes_and_gives_back 中,// 它也将返回值移给 s3
} // 这里,s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,// 所以什么也不会发生。s1 离开作用域并被丢弃
​
fn gives_ownership() -> String {             // gives_ownership 会将// 返回值移动给// 调用它的函数
​let some_string = String::from("yours"); // some_string 进入作用域。
​some_string                              // 返回 some_string // 并移出给调用的函数// 
}
​
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域// 
​a_string  // 返回 a_string 并移出给调用的函数
}

变量的所有权总是遵循相同的模式:将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过 drop 被清理掉,除非数据被移动为另一个变量所有。

虽然这样是可以的,但是在每一个函数中都获取所有权并接着返回所有权有些啰嗦。如果我们想要函数使用一个值但不获取所有权该怎么办呢?如果我们还要接着使用它的话,每次都传进去再返回来就有点烦人了,除此之外,我们也可能想返回函数体中产生的一些数据。

我们可以使用元组来返回多个值, 看下面的代码:

fn main() {let s1 = String::from("hello");
​let (s2, len) = calculate_length(s1);
​println!("The length of '{}' is {}.", s2, len);
}
​
fn calculate_length(s: String) -> (String, usize) {let length = s.len(); // len() 返回字符串的长度
​(s, length)
}

但是这未免有些形式主义,而且这种场景应该很常见。幸运的是,Rust 对此提供了一个不用获取所有权就可以使用值的功能,叫做 引用references)。

咱们下一章将揭开引用与借用的神秘面纱。

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

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

相关文章

Mac苹果电脑分辨率修改管理 安装SwitchResX 完美解决

SwitchResX for Mac是一款Mac应用程序,可帮助您更好地管理和控制显示器分辨率和其他显示设置。使用SwitchResX,您可以创建自定义分辨率、旋转屏幕、调整显示器色彩配置等。 1. 自定义分辨率:SwitchResX允许用户创建自定义的屏幕分辨率&#…

VSCode设置中文语言界面(VScode设置其他语言界面)

一、下载中文插件 二、修改配置 1、使用快捷键 CtrlShiftP 显示出搜索框 2、然后输入 configure display language 3、点击 (中文简体) 需要修改的语言配置 三、重启 四、可能出现的问题 1、如果configure display language已经是中文配置,界面仍是英文 解决&a…

css进阶知识点速览

0前言 零基础部分的博客 1选择器进阶 1.1后代选择器 作用:根据html标签的嵌套关系,选择父元素后代中满足条件的元素 选择器语法:选择器1 选择器2 {css} 结果: 在选择器1所找到标签的后代中 注意: 后代包括&#xf…

python 视频硬字幕去除 内嵌字幕去除工具 vsr

项目简介 开源地址:https://github.com/YaoFANGUK/video-subtitle-remover Video-subtitle-remover (VSR) 是一款基于AI技术,将视频中的硬字幕去除的软件。 主要实现了以下功能: 无损分辨率将视频中的硬字幕去除,生成去除字幕后…

java高级之单元测试、反射

1、Junit测试工具 Test定义测试方法 1.被BeforeClass标记的方法,执行在所有方法之前 2.被AfterCalss标记的方法,执行在所有方法之后 3.被Before标记的方法,执行在每一个Test方法之前 4.被After标记的方法,执行在每一个Test方法之后 public …

取消elementUI中table的选中状态和勾选状态赋值

一、取消所有选中 1、表格上绑定ref 2、清空用户选中数据 this.$refs.loopRef.clearSelection()二、勾选状态赋值 获取数据,flag为true则是选中状态,并将前面勾选框设为选中状态 this.listData.forEach(item> {if(row.flag1){this.$refs.loopRef.to…

JavaEE平台技术——预备知识(Maven、Docker)

JavaEE平台技术——预备知识(Maven、Docker) 1. Maven2. Docker 在观看这个之前,大家请查阅前序内容。 😀JavaEE的渊源 😀😀JavaEE平台技术——预备知识(Web、Sevlet、Tomcat) 1. M…

【漏洞复现】typecho_v1.0-14.10.10_unserialize

感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 漏洞利用GetShell 下载链接:https://pan.baidu.com/s/1z0w7ret-uXHMuOZpGYDVlw 提取码:lt7a 首页 漏洞点:/install.php?finish 漏洞利用 …

5+单基因泛癌范文式教学,适合小白学习

今天给同学们分享一篇生信文章“Comprehensive Pan-Cancer Analysis of KIF18A as a Marker for Prognosis and Immunity”,这篇文章发表在Biomolecules期刊上,影响因子为5.5。 结果解读: KIF18A的表达及其在泛癌中的诊断价值 TIMER数据库被…

多模态中各种Fusion方式汇总

多模态中各种Fusion骚操作 大噶好,我是DASOU; 今天继续写多模态系列文章,对多模态感兴趣的可以看我之前的文章: 其实对于多模态来说,主要可以从三个部分去掌握它: 如何获取多模态的表示【learning mult…

【ARFoundation学习笔记】ARFoundation基础(下)

写在前面的话 本系列笔记旨在记录作者在学习Unity中的AR开发过程中需要记录的问题和知识点。难免出现纰漏,更多详细内容请阅读原文。 文章目录 TrackablesTrackableManager可跟踪对象事件管理可跟踪对象 Session管理 Trackables 在AR Foundation中,平面…

GoLong的学习之路(番外)如何使用依赖注入工具:wire

我为什么要直接写番外呢?其原因很简单。项目中会使用,其实在这里大家就可以写一些项目来了。 依赖注入的工具本质思想其实都大差不差。无非控制反转和依赖注入。 文章目录 控制反转为什么需要依赖注入工具 wire的概念提供者(provider&#x…

【计算机网络笔记】TCP的拥塞控制机制

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

Linux文件描述符和打开文件之间的关系

简介 文件描述符和打开的文件之间似乎呈现出一一对应的关系。然而,实际并非如此。多个文件描述符指向同一打开文件,这既有可能,也属必要。这些文件描述符可在相同或不同的进程中打开。 要理解具体情况如何,需要查看由内核维护的…

应用安全四十二:SSO安全

一、什么是SSO SSO是单点登录(Single Sign On)的缩写,是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的企业业务整合的解决方案之一。 身份验证过程依赖于双方之间的信任关…

Spring Boot创建多模块项目

创建一个普通的Spring Boot项目, 然后只留下 pom.xml 剩下的都删掉 删除多余标签 标识当前为父模块 创建子模块 删除子模块中多余标签 声明父模块 在父模块中声明子模块

基于JAVA+SSM的房屋租赁系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取项目下载方式🍅 一、项目背景介绍: 随着社会的发展和人们…

【C/C++】什么是POD(Plain Old Data)类型

2023年11月6日,周一下午 目录 POD类型的定义标量类型POD类型的特点POD类型的例子整数类型:C 风格的结构体:数组:C 风格的字符串:std::array:使用 memcpy 对 POD 类型进行复制把POD类型存储到文件中,并从文…

webgoat-(A1)SQL Injection

SQL Injection (intro) SQL 命令主要分为三类: 数据操作语言 (DML)DML 语句可用于请求记录 (SELECT)、添加记录 (INSERT)、删除记录 (DELETE) 和修改现有记录 &#xff…

【自我提升】项目升级-Beyond Compare效率工具

写在前面:最近接手一个项目,这个项目集成了许多开源项目,其中我需要对其中一个开源项目进行升级操作。在此记录升级过程和心得,希望可以给各位道友提供一种思路,同时也希望道友有更好的办法能分享在评论区。 项目场景 …