Rust 圣经 阅读 所有权和借用

所有权

栈(Stack)与堆(Heap)

栈何和堆的核心目标就是为程序在运行时提供可供使用的内存空间。

栈按照顺序存储值并以相反顺序取出值,后进先出。
增加数据叫进栈,取出数据叫出栈。
栈中的所有数据必须占用 已知且固定大小的空间。假设数据大小是未知的,那么在取出数据时,将不能取出想要的数据。

对于大小未知或可能变化的数据,我们需要将其存储在堆上。

当向堆上存储数据时,需要请求一定大小的空间,然后操作系统在堆的某处找到符合大小的空间,将其标注为已使用,并返回一个表示该位置地址的指针。该过程被称为在堆上分配内存。有时简称为”分配(allocation)“。

返回的指针会被推入栈中,因为指针的大小是已知且固定的,在后续使用中,可以通过指针,来获取数据在堆上的实际内存位置,进而访问数据。

堆上的数据是无组织的。

性能区别

写入方面:入栈比在堆上分配内存要快。
因为入栈只需将新数据放在栈顶,而在堆上分配内存需要找到足够存放数据空间,还要做一些记录为下次分配做准备。

读取方面:出栈比从堆上读取数据更快。
首先,栈上的数据都是存储在CPU的高速缓存上,而堆上的数据只能存储在内存中。而高速缓存和内存的访问速度差异在10倍以上。
其次,访问堆上数据,必须先访问栈再通过栈上的指针来访问内存。

所有权和堆栈

当调用一个函数,传递给函数的参数(包括指向堆的指针和函数的局部变量)一次压入栈中,当函数调用结束时,这些值将中栈中按照相反的顺序依次移除。
所有权帮助避免内存泄漏。

所有权原则

规则:

  1. Rust 中每一个值都被一个变量所拥有,该变量称为值的拥有者。
  2. 一个值同时只能被一个变量拥有,一个值只能有一个拥有者。
  3. 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)。
String 类型

动态字符串类型: String,该类型被分配到堆上,因此可以动态伸缩,可以存储在编译时大小未知的文本。

基于字符串字面量来创建 String 类型:

let s = String::from("hello");

:: 是一种调用操作符,表示调用 String 中的 from 方法,因为 String 存储在堆上的动态的,所有它可以修改。

let mut s = String::from("hello");s.push_str(", world!"); // push_str() 在字符串后追加字面值println!("{}", s); // 将打印 `hello, world!`

变量绑定背后的数据交互

转移所有权
let x= 5;
let y = x;

5 绑定到变量 x;接着拷贝 x 的值赋给 y,最终 xy 都等于 5,因为整数是 Rust 基本数据类型,是固定大小的简单值,因此这两个值都是通过自动拷贝的方式来赋值的,都被存在栈中,完全无需在堆上分配内存。

只有存储在栈上的基本类型,rust 会自动拷贝。

let s1 = String::from("hello");
let s2 = s1;

String 不是基本类型,而且是存储在堆上的,因此不能自动拷贝。

实际上, String 类型是一个复杂类型,由存储在栈中的堆指针字符串长度字符串容量共同组成,其中堆指针是最重要的,它指向了真实存储字符串内容的堆内存,至于长度和容量,容量是堆内存分配空间的大小,长度是目前已经使用的大小。

这里有两种情况:

  1. 深拷贝:拷贝了所有数据,包含堆上的数据和栈上的数据。对性能有很大的影响。
  2. 只拷贝了 String,即仅拷贝指针、长度、容量。但是这里就涉及到了所有权的问题,因为一个值只允许有一个所有者。这样的拷贝会诞生两个所有者。多个所有权可能会导致多次释放同一个内存,会导致内存污染

Rust 在当 s1 赋予 s2 后,Rust 认为 s1 不再有效,所有在 s1 离开作用域后不会 drop 任何东西。
当赋予后,原来的所有者会成为无效的引用。

这样的浅拷贝在Rust称为移动,因为原有的拥有者失效了。

fn main() {let x: &str = "hello, world";let y = x;println!("{},{}",x,y);
}

这段代码不会出问题,是因为x并不是字符串的拥有者,它只是引用了这个字面量。仅仅是对该引用进行了拷贝,二者都引用了同一个字符串。

克隆(深拷贝)

Rust 不会自动创建数据的深拷贝。 任何自动的复制都不是深拷贝。
可以使用 clone 方法来深度复制 string 中堆上的数据,而不仅仅是栈上的数据。

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

克隆完整地复制了 s1 的数据,他们是两个不同的数据的所有者,所以不会出现错误。
频繁地使用克隆会降低程序的性能。

拷贝(浅拷贝)

浅拷贝只发生在栈上,因此性能很高。
基本类型在编译时是已知大小的,会被存储在栈上,这里的深浅拷贝没有区别。

copy

Rust 有一个叫做 copy 的特征,可以用在类型整型这样在栈上存储的变量。
当一个类型拥有 copy 特征,一个旧的变量被赋给其他变量后依然可用。

具体一个类型是否可以 copy 需要查看给定的文档来确认。
通用规则是 任何基本类型的组合可以copy,不需要分配内存或某种形式资源的类型可以 copy
下面是一些可以 copy 的类型:

  • 所有整数类型,比如 u32
  • 布尔类型, bool
  • 所有浮点数类型,比如 f64
  • 字符类型, char
  • 元组,当且仅当其包含的类型也都是 Copy 的时候。比如, (i32, i32)Copy 的,但 (i32, String) 就不是。
  • 不可变引用 &T,但是 可变引用 &mut T 是不可以 Copy 的

函数传值与返回

将值传递给函数,一样会发生 移动复制

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 移出作用域。不会有特殊操作

函数返回值也有所有权。

fn main() {let s1 = gives_ownership();         // gives_ownership 将返回值// 移给 s1let 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("hello"); // 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 并移出给调用的函数
}

所有权很强大,避免了内存的不安全性,但是也带来了一个新麻烦: 总是把一个值传来传去来使用它。 传入一个函数,很可能还要从该函数传出去,结果就是语言表达变得非常啰嗦,幸运的是,Rust 提供了新功能解决这个问题。

原来的可变变量在转移所有权时,是否可变取决于新的变量。


fn main() {let mut s = String::from("hello, ");s.push_str("world");println!("{:?}",s);// 只修改下面这行代码 !let  s1 = s; // 需要添加 muts1.push_str("world");println!("{:?}",s1);
}

可以使用 ref 来引用一个变量,引用的不是值,而是变量,而不是转移所有权。


fn main() {#[derive(Debug)]struct Person {name: String,age: Box<u8>,}let person = Person {name: String::from("Alice"),age: Box::new(20),};// 通过这种解构式模式匹配,person.name 的所有权被转移给新的变量 `name`// 但是,这里 `age` 变量却是对 person.age 的引用, 这里 ref 的使用相当于: let age = &person.age let Person { name, ref age } = person;println!("The person's age is {}", age);println!("The person's name is {}", name);// Error! 原因是 person 的一部分已经被转移了所有权,因此我们无法再使用它//println!("The person struct is {:?}", person);// 虽然 `person` 作为一个整体无法再被使用,但是 `person.age` 依然可以使用println!("The person's age from person struct is {}", person.age);
}
![请添加图片描述](https://img-blog.csdnimg.cn/direct/c7f3d6a453674aae992f125bf7af6c2d.png)

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

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

相关文章

算法训练营第三十天|332.重新安排行程 51. N皇后 37. 解数独

目录 Leetcode332.重新安排行程Leetcode51. N皇后Leetcode37. 解数独 Leetcode332.重新安排行程 文章链接&#xff1a;代码随想录 题目链接&#xff1a;332.重新安排行程 class Solution { public:unordered_map<string, map<string, int>> targets;bool backtrack…

部署清华ChatGLM-6B(Linux版)

引言 前段时间,清华公布了中英双语对话模型 ChatGLM-6B,具有60亿的参数,初具问答和对话功能。最!最!最重要的是它能够支持私有化部署,大部分实验室的服务器基本上都能跑起来。因为条件特殊,实验室网络不通,那么如何进行离线部署呢? 「部署环境」:CUDA Version 11.0,…

Maven简介及环境搭建和基本使用(Java开发中的实用工具)

一、概述 Maven 是 Apache 软件基金会的一个开源项目,是一个优秀的项目构建工具,它 用来帮助开发者管理项目中的 jar,以及 jar 之间的依赖关系、完成项目的编译、 测试、打包和发布等工作。 Maven的相关概念 pom.xml文件&#xff1a;里面可以配置相关信息&#xff0c;指导ma…

【SpringBoot开发】之商城项目案例(沙箱支付)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发之商城项目系列》。&#x1f3af…

MySQL数据库索引优化实战

目录 一、前言 二、准备工作 2.1 用户表&#xff08;TB_USER) 2.2 商品表&#xff08;TB_SKU) 2.3 订单表&#xff08;TB_ORDER&#xff09; 三、实例分析 3.1 索引提升查询性能 3.2 多表查询 3.3 索引失效 四、总结 一、前言 在数据库的应用中&#xff0c;性能优化…

Java单词排序

【问题描述】 编写一个程序&#xff0c;从一个文件中读入单词&#xff08;即&#xff1a;以空格分隔的字符串&#xff09;&#xff0c;并对单词进行排序&#xff0c;删除重复出现的单词&#xff0c;然后将结果输出到另一个文件中。 【输入形式】从一个文件sort.in中读入单词。 …

类加载机制之双亲委派模型、作用、源码、SPI打破双亲委派模型

双亲委派模型 双亲委派工作机制双亲委派的作用双亲委派的实现源码SPI打破双亲委派 应用程序是由三种类加载器相互配合&#xff0c;从而实现类加载&#xff0c;除此之外还可以加入自己定义的类的加载器。 类加载器之间的层次关系&#xff0c;称为双亲委派模型&#xff08;Parent…

2024最全面且有知识深度的web3开发工具、web3学习项目资源平台

在Web3技术迅速发展的时代&#xff0c;寻找一个综合且深入的Web3开发工具和学习项目资源平台变得至关重要。今天&#xff0c;我将向大家介绍一个非常有价值的网站&#xff0c;它就是https://web3x.world 。 Web3X是一个全面而深入的Web3开发者社区&#xff0c;为开发者们提供了…

【深度学习】各领域常用的损失函数汇总(2024最新版)

目录 1、L1 损失、平均绝对误差&#xff08;L1 Loss、Mean Absolute Error&#xff0c;MAE&#xff09; 2、L2 损失、均方误差&#xff08;L2 Loss、Mean Squared Error&#xff0c;MSE&#xff09; 3、交叉熵损失&#xff08;Cross-Entropy Loss&#xff09; 4、混合损失&…

webpack的深入学习与实战(持续更新)

一、何为Webpack Webpack是 一个开源的JavaScript模块打包工具&#xff0c;其最核心的功能是解决模块之间的依赖&#xff0c;把各个模块按照特定的规则和顺序组织在一起&#xff0c;最终合并为一个JS文件或多个。 二、带宽的换算 目前我们的云服务器带宽为5M 三 、bundle 体…

【算法挨揍日记】day34——647. 回文子串、5. 最长回文子串

647. 回文子串 647. 回文子串 题目描述&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&am…

202365读书笔记|《温暖和百感交集的旅程》——时间帮助着一个人的一生在几千字的篇幅里栩栩如生

202365读书笔记|《温暖和百感交集的旅程》——时间帮助着一个人的一生在几千字的篇幅里栩栩如生 我能否相信自己温暖和百感交集的旅程前言和后记 《温暖和百感交集的旅程》作者余华&#xff0c;之前读完的一本书&#xff0c;已经不太记得内容了&#xff0c;看着笔记整理的这篇书…

用数据结构python写大数计算器

下面是一个基于Python的大数计算器的示例代码&#xff1a; class BigNumberCalculator:def __init__(self, num1, num2):self.num1 num1self.num2 num2staticmethoddef add(num1, num2):result carry 0len1, len2 len(num1), len(num2)max_len max(len1, len2)for i in …

【进程间通信】

什么是进程通信 进程通信&#xff08; InterProcess Communication&#xff0c;IPC&#xff09;就是指进程之间的信息的传播和交换。 进程是分配系统资源的单位&#xff0c;包括内存地址空间&#xff0c;为了保证安全&#xff0c;一个进程不能直接访问另一个进程的地址空间&a…

KLR技术支持工程师笔试题(包含Linux、计网知识)

文章目录 前言一、选择题总结 前言 深圳市KLRKJYXGS技术支持工程师笔试题博主回忆版&#xff0c;仅供大家学习。 一、选择题 1、Linux 系统中 DNS 服务进程名为&#xff08; &#xff09; A. named B. httpd C. ftpd D. SysLog 【答案】A 【解析】 2、在 UINX/Linux 中&#…

PO模式。

对项目进行梳理和总结 1.整个项目中的文件解析 2.每个文件包和模块具体的使用规范 3.整个项目中的模块与文件之间的联系 4.各个模块及包之间的导入使用 5.整体从项目完成之后的角度出发去将军项目搭建流程以及使用规范 搭建app自动化测试环境 安卓的sdk java的sdk 安装app…

instant ngp win11 安装笔记

目录 训练保姆级教程: instant ngp安装参考&#xff1a; 编译步骤1 编译步骤2 我把编译成功的库分享到百度网盘了 训练保姆级教程: 英伟达NeRF项目Instant-ngp在Windows下的部署&#xff0c;以及数据集的制作&#xff08;适合小白的保姆级教学&#xff09;_colmap2nerf.p…

2023我的 python 编程之旅:从入门到熟练我用了5年,从构思到发文只用了3个月!

我与python的初次相见 一切都是大数据做主。当时我记得很清楚&#xff0c;是19年初&#xff0c;正值准备大学毕业和研究生入学前的一段空闲期。那个时候总会有各种形式的编程语言广告进入我的视野&#xff0c;勾起了被C语言伤害的痛苦和不甘。正好有时间和精力&#xff0c;再加…

CMake入门教程【核心篇】属性管理set_property和get_property

&#x1f608;「CSDN主页」&#xff1a;传送门 &#x1f608;「Bilibil首页」&#xff1a;传送门 &#x1f608;「本文的内容」&#xff1a;CMake入门教程 &#x1f608;「动动你的小手」&#xff1a;点赞&#x1f44d;收藏⭐️评论&#x1f4dd; 文章目录 1.概述2.设置属性 - …

FPGA设计时序约束十五、Set_Bus_Skew

目录 一、序言 二、Set Bus Skew 2.1 基本概念 2.2 设置界面 2.3 命令语法 2.4 报告分析 三、工程示例 3.1 工程代码 3.2 时序报告 四、参考资料 一、序言 在时序约束中&#xff0c;对时钟的约束除了set clock latency,set clock uncertainty,set input jitter外&…