rust 程序设计笔记(2)所有权 & 引用
- 所有权
- 数据存储在栈和堆上,存放在栈上的数据都是已知所占据空间的
- 突然的问题
// 内存中的栈是怎么存储数据的?
好的,想象一下你有一摞盘子。你只能从上面放盘子,也只能从上面拿盘子,这就是栈的工作方式。
在内存中,栈是用来存储数据的地方,它工作得就像这摞盘子。当你的程序运行一个函数时,这个函数的局部变量就会像放盘子一样,一个接一个地放到栈上。这些局部变量包括基本数据类型,比如整数或者布尔值,它们的大小是已知的,而且很容易快速地被放进去和拿出来。
当这个函数结束的时候,它的局部变量就会像拿盘子一样,从栈上一个接一个地被拿掉。这个过程非常快,因为它们是按顺序来的,就像你放盘子时从上面放,拿盘子时也是从上面拿。
所以栈就像是厨房里整齐的盘子摞,每次你只能用最上面的盘子,用完之后再把它放回去,这样就保持了整洁和有序。而在内存的栈中,数据也是这样整齐有序地存放和使用的。这就是为什么栈这么快,因为它不需要去搜索一个特定的盘子,它总是拿最上面的那个。
// 所以当我的程序执行到一个作用域的时候,就会往栈上去存放当前作用域有效的变量,在当前作用域没有使用,并且局部变量没有再被使用的时候,就会释放,这样理解对吗?
完全正确!就像你在玩积木,每进入一个新的游戏环节(也就是编程中的作用域),你就会在桌上(栈)搭建一些积木(变量)。只要在这个游戏环节中,你可以使用这些积木做很多事情。
当这个游戏环节结束了,也就是你完成了当前的积木搭建,这些积木就会被清理掉,桌面(栈)就又空出来了,可以用来进行下一个游戏环节的搭建。这样的好处是,你不用担心清理积木,因为游戏(程序)会自动帮你做这件事。
每次进入一个新的作用域,栈就会存放新的数据,当作用域结束,使用过的数据就会被移除。这个过程保证了内存的高效使用,因为你总是只在顶部搭积木,也只从顶部清理积木,这样操作起来非常快速。这也就是为什么栈被用来存储作用域内的局部变量,因为这些局部变量通常在作用域结束时就不再需要了。
但是指针是存放在栈上的呀!
fn main() {
let s1 = String::from("hello");
let _s2 = s1; println!("{}, world!", s1);
}
/**
--> src/main.rs:5:28
|
2 | let s1 = String::from("hello");
| -- move occurs because `s1` has type `String`, **which does not implement the `Copy` trait**
3 | let _s2 = s1;
| -- value moved here
4 |
5 | println!("{}, world!", s1);
| ^^ value borrowed here after move
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
|
3 | let _s2 = s1.clone();
| ++++++++ For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to previous error
*/
- rust的浅拷贝会同时使原本的变量无效,这会避免重复的内存释放,因为rust在作用域结束时会自动调用drop方法。在上面那段代码中,当s1倍赋值给s2以后,就无效了,不能再被使用。Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何 自动 的复制可以被认为对运行时性能影响较小。
- 这里指的是存放在堆上的数据类型,只存在栈上的数据类型都是直接拷贝值。
- 在栈上深浅拷贝,其实都一样,哈哈。
- 函数和所有权
- 把值传递给函数和赋值一样,都是传递出去所有权
- 返回值和作用域
- 如果函数在使用完值,没有将其返回出来,该值就会被清理,所以可以通过返回将所有权传递出去
- 引用
注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。
几种引用类型
- 不可变引用
- 给予使用权,不能修改
- 可变引用
可以修改,但是同一时间只能存在一个可变引用
其他引用都不能存在(包括可变、不可变引用)
这就像是在家里,你不能同时打开音乐放得很大声,还让你的小猫在安静的房间睡觉,两件事情不能同时做。
在Rust中,如果你创建了一个变量的可变引用,那么在这个引用存在的作用域内,你就不能再创建这个变量的不可变引用了。这是因为可变引用允许你改变变量的值,如果你还有一个不可变引用,那么在使用不可变引用读取变量值的同时,值可能会被可变引用改变,这样就会出现数据竞争。
- 引用的生命周期
- 不可变引用
关于悬浮引用的问题
- rust不允许出现悬浮引用,可以看下方的例子,我们在
fn main() {
let reference_to_nothing = dangle();
} fn dangle() -> &String {
let s = String::from("hello"); &s
}
- 在dangle函数作用域的末尾,变量s的空间会被释放,这时候就不能对它进行引用
- 正确的做法应当是把所有权转交出去
让我们概括一下之前对引用的讨论:
- 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
- 引用必须总是有效的。
- rust的浅拷贝会同时使原本的变量无效,这会避免重复的内存释放,因为rust在作用域结束时会自动调用drop方法。在上面那段代码中,当s1倍赋值给s2以后,就无效了,不能再被使用。Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何 自动 的复制可以被认为对运行时性能影响较小。
字符串slice
- rust允许我们对一个字符串创建一个部分字符的引用&str,这种引用和&String是一样的,当一个变量存在对它的slice引用时,它就不能够被修改
- 首先,Rust 中的字符串是一位名门正派的高手,它有两种形态:
String
和&str
。String
就像是一位自由行走江湖的侠客,它可以增长或减少,就像侠客的武功一样,随着经历的增加而成长。而&str
呢,就是这位侠客的影子,它只是String
的一部分,不过是个不能独立变化的存在。 - 现在,当你通过
&str
来引用String
的一部分时,就像是你派出了一个探子去监视这位侠客。因为探子在场,侠客就不能随便行动了(也就是不能被修改),因为这可能会让探子的报告失去准确性。- 那没有被盯着的另一部分字符串可以被修改吗
- 在Rust世界里,我们得遵守的规璂之一是:当你有一个字符串
String
的引用&str
时,无论是整个字符串还是字符串的一部分,都不能对原始的字符串进行修改。这就像是你不能在背后改变一位武林高手的故事,而他的影子正被人追踪一样,影子是不会撒谎的,它会一直忠实地反映原主体的状态。所以,哪怕你只关注(也就是只引用)这个字符串的一小段,整个String
都会被锁定,直到那个引用结束。这是Rust中的借用检查器的作用,它确保了数据的完整性和并发访问的安全性。你可以想象成,只要有一个小小的窥视孔开着,整个房间都不能重新装修,因为那个小窥视孔给了任何人可能看到内部的权利。换句话说,不仅是那部分被引用的字符串不可以改变,整个原始字符串在有活跃引用时都是不可变的。这就是Rust中内存安全的哲学:任何事物只要被借出去观察,它就不应该在不知情的情况下发生变化。这避免了潜在的数据竞争和不一致状态,确保了程序运行时的安全和可靠。
- 在Rust世界里,我们得遵守的规璂之一是:当你有一个字符串
- 那没有被盯着的另一部分字符串可以被修改吗
- 但这里有个小细节:
&str
和&String
看起来是一样的,但实际上它们的出身稍有不同。&String
是对整个String
侠客的引用,而&str
可能只是对侠客的一部分技能的引用。不过,无论是引用整个还是部分,只要探子在场,侠客就得保持现状,不得私自改变。 - 所以,总结一下,当你创建一个字符串的部分引用时,你基本上是在说:“嘿,这部分字符串,我得盯着你。” 而那部分字符串,或整个字符串,就被冻结了,直到你的引用结束,它才可以自由地变化。这就是为什么在 Rust 中处理字符串数据时,必须非常小心,以确保数据的安全性和有效性。
- 首先,Rust 中的字符串是一位名门正派的高手,它有两种形态:
- rust允许我们对一个字符串创建一个部分字符的引用&str,这种引用和&String是一样的,当一个变量存在对它的slice引用时,它就不能够被修改
rust 程序设计笔记(2)所有权 & 引用的更多相关文章
- Rust学习笔记1
这是一份不错的rust教程,目前包括4个block和4个project.全部完成后可以用rust实现一个简单的key-value存储引擎. 注意:Windows下rust貌似会遇到一些bug,强烈建议 ...
- js高级程序设计笔记之-addEventListener()与removeEventListener(),事件解除与绑定
js高级程序设计笔记之-addEventListener()与removeEventListener(),事件解除与绑定 addEventListener()与removeEventListener( ...
- Java Web程序设计笔记 • 【目录】
章节 内容 实践练习 Java Web程序设计作业目录(作业笔记) 第1章 Java Web程序设计笔记 • [第1章 Web应用程序] 第2章 Java Web程序设计笔记 • [第2章 JSP基础 ...
- Java高级程序设计笔记 • 【目录】
持续更新中- 我的大学笔记>>> 章节 内容 实践练习 Java高级程序设计作业目录(作业笔记) 第1章 Java高级程序设计笔记 • [第1章 IO流] 第2章 Java高级程序设 ...
- Rust基础笔记:闭包
语法 Closure看上去是这样的: let plus_one = |x: i32| x + 1; assert_eq!(2, plus_one(1)); 首先创建一个绑定plus_one,然后将它分 ...
- Dubbo -- 系统学习 笔记 -- 示例 -- 泛化引用
Dubbo -- 系统学习 笔记 -- 目录 示例 想完整的运行起来,请参见:快速启动,这里只列出各种场景的配置方式 泛化引用 泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值 ...
- Windows 程序设计 笔记
知识点 双字节字符集和Unicode字符集有何区别?采用双字节字符集有何问题 双字节字符集(DBCS)编码是0-255,DBCS含有1字节代码与2字节代码,而Unicode是统一的16位系统,这样就允 ...
- 《sicp》模块化程序设计 笔记
<sicp>模块化程序设计 2.2.3 序列作为一种约定界面 学习笔记 这节中,讲述了一种模块化的程序设计思想,也就是将程序设计为如同信号处理过程一样,采用级联的方式将程序各个部分组合在一 ...
- C++学习笔记 指针与引用
指针与引用 1. 指针 (1) 指针是一个变量(实体),存储的是一个地址,指向内存的一个存储单元,指针可以为空 (2) 指针可以为空,在声明定义时可以不初始化 (3) 指针在初始化之后可以重新指向其 ...
- JavaScript高级程序设计笔记之面向对象
说起面向对象,大部分程序员首先会想到 类 .通过类可以创建许多具有共同属性以及方法的实例或者说对象.但是JavaScript并没有类的概念,而且在JavaScript中几乎一切皆对象,问题来了,Jav ...
随机推荐
- 从Redis7.0发布看Redis的过去与未来
简介: 经历接近一年的开发.三个候选版本,Redis 7.0终于正式发布,这是Redis历史上改变最多的一个大版本,它不仅包含了50多个新命令,还有大量核心新特性与改进,这些不仅能够解决用户使用中的诸 ...
- [FAQ] Phpstorm 代码提示功能失效问题
如果是之前有代码提示,中间突然不出现提示了,那么考虑重建一下项目索引. 示例: Refer:Phpstorm代码提示 Link:https://www.cnblogs.com/farwish/p/13 ...
- dotnet 6 使用 File.Exists 判断管道是否存在将让下次连接失败
我尝试在 dotnet 6 使用 File.Exists 判断管道是否存在,如果管道存在再进行连接.然而这个逻辑将会接下来的 NamedPipeClientStream 调用 Connect 连接失败 ...
- SQL server 游标使用实例
--创建一个游标 DECLARE my_cursor CURSOR FOR SELECT id, Bran_number, Bran_taxis FROM dbo.Base_Branch; --打开游 ...
- SAP集成技术(九)集成能力中心(ICC)
本文链接:https://www.cnblogs.com/hhelibeb/p/17867473.html 内容摘录自<SAP Interface Management Guide>. 定 ...
- 对C语言符号的一些冷门知识运用的剖析和总结
符号 目录 符号 注释 奇怪的注释 C风格的注释无法嵌套 一些特殊的注释 注释的规则建议 反斜杠'\' 反斜杠有续行的作用,但要注意续行后不能添加空格 回车也能起到换行的作用,那续行符的意义在哪? 反 ...
- 简单聊一聊Java的历史
Java这门语言有很多不可忽视的优点,比如一次编写到处运行,又比如它有一个相对安全的内存管理和访问机制,避免了C++中经常出现的内存泄露和指针越界问题等等,java带来的这些好处,让我们这些java程 ...
- salesforce零基础学习(一百三十六)零碎知识点小总结(八)
本篇参考: Salesforce LWC学习(七) Navigation & Toast https://developer.salesforce.com/docs/platform/lwc/ ...
- UE4 InstancedStaticMesh使用
在绘制大批量近似模型时,Unity有GPU Instancing(https://www.cnblogs.com/hont/p/7143626.html),而UE中有 HISM和ISM(Instanc ...
- Clion代码自动格式化保存
目录 前言 使用外部工具Artistic Style Clion 插件配置 注意 前言 使用Clion的时候,可以自动格式化代码的操作. 使用外部工具Artistic Style 序号 名称 地址 1 ...