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 ...
随机推荐
- 一个 Blink 小白的成长之路
写在前面 写过blink sql的同学应该都有体会,明明写的时候就很顺滑,小手一抖,洋洋洒洒三百行代码,一气呵成.结果跑的时候,吞吐量就是上不去.导致数据延迟高,消息严重积压,被业务方疯狂吐槽.这时候 ...
- 大型 Web 应用插件化架构探索
简介: 随着 Web 技术的逐渐成熟,越来越多的应用架构趋向于复杂,例如阿里云等巨型控制台项目,每个产品下都有各自的团队来负责维护和迭代.不论是维护还是发布以及管控成本都随着业务体量的增长而逐渐不可控 ...
- Datastream 开发打包问题
简介:Datastream作业开发时往往会遇到一些jar包冲突等问题,本文主要讲解作业开发时需要引入哪些依赖以及哪些需要被打包进作业的jar中,从而避免不必要的依赖被打入了作业jar中以及可能产生的 ...
- [ML] 机器学习的 7 步流程
Gathering Data. Preparing that Data. Choosing a Model. Training. Evaluation. Hyperparameter Tuning. ...
- [FAQ] Error: Component series.bar not exists. Load it first. (echarts)
以上错误出现在使用 echarts 组件时,未导入或者使用不正确的情况下. 检查是否导入 line 或者 bar 这一类具体的 chart,比如: import 'echarts/lib/chart/ ...
- dotnet 在 UOS 国产系统上使用 MonoDevelop 进行拖控件开发 GTK 应用
先从一个 Hello World 应用开始,试试和古老的 WinForms 一样的拖控件式开发 在创建完成一个 GTK# 2.0 应用之后,咱可以试试开始拖控件的开发,当然这个开发方式开发出来的应用界 ...
- 从改一个老项目开始的PHP踩坑记
php所有版本的地址: https://windows.php.net/downloads/releases/archives/ 访问控制器时省略了index.php报No input file sp ...
- Etcd 可视化管理工具,GUI 客户端。
Etcd Assistant--Etcd 可视化管理工具,GUI 客户端. 下载地址:http://www.redisant.cn/etcd 主要功能: 支持多标签页,同时连接到多个集群 以漂亮的格式 ...
- netcore5下ocelot网关简单使用
1.新建aspnetcoremvc项目,带home控制器的就可以了,测试用能启动就行,代码无需做任何更改. 2.新建空的aspnetcoremvc项目,做如下更改: 1.. 2.. 3.. 4.. ...
- 五:瑞芯微RV1109
瑞芯微RV1109是一款用于工控机或人工智能视觉应用的高性能机器视觉处理器SoC. 参考资料 http://www.neardi.com/news_22/434.html https://www.ro ...