当前位置:网站首页>每日一R「02」所有权与 Move 语义
每日一R「02」所有权与 Move 语义
2022-08-09 21:54:00 【InfoQ】
01-所有权原则
- c / c++ 中,堆内存的分配和释放,需要开发者自觉地去维护,使用 malloc / free 来显式地申请和释放堆内存。这样最大的问题就在于,对内存管理完全交由开发者自行维护,而人总是容易犯错的,极容易发生内存泄漏等问题。
- java 采用了与 c / c++ 不一样的方式来管理堆内存。当 new 关键字时,JVM 会帮助开发者在堆上分配空间。堆空间的回收 JVM 通过 GC 来完成。这样做虽然可以很大程度上确保内存安全,但 GC 需要消耗额外的资源,而且还存在 Stop the world 的问题。
- 一个值只能被一个变量所拥有,这个变量被称为所有者。
- 一个值同一时刻只能有一个所有者。
- 当所有者离开作用域,其拥有的值被丢弃。
- 栈内存。
- 栈中存储的是固定长度的类型值,例如i32 / u32 / u8 等。对于这块空间中的内容,它的所有权由某个变量拥有,这个变量称为这块内存的所有者(第一条)。例如
let x: u32 = 5;
会在栈空间中分配 32 位长度的内存,并将这块内存的内容设置为 5。同样,let y: u8 = 1;
会在栈空间中分配 8 位长度的内存,并将这块内存的内容设置为 1。
- 所有权第二条怎么理解呢?就是说栈空间中的某块内存(值)同一时刻只能被一个变量拥有。从代码上我们理解一下:
let x: u32 = 10; let y = x;
前面一句与上面一样,在栈空间中分配一块内存,并将这块内存的所有权给 x。第二句会在栈空间中分配同样 x 同样大小的空间并将 x 中的内容拷贝一份到这块空间(Copy 语义,会在后面的课程中学习到)。y 是这块新空间的所有者,与所有权原则第一条自洽。
- 变量的作用域指从变量声明开始,到变量所在代码块结束这段时间。当变量离开其作用域,变量拥有的内存空间被释放,这点也不难理解。
- 堆内存。
- 堆中存储的是长度可变的值,例如 Vec,String等。对于这块内存空间,变量是通过什么方式来体现所有权的呢?《Rust语言圣经》是这样描述的:
String
类型是一个复杂类型,由存储在栈中的堆指针、字符串长度、字符串容量共同组成,其中堆指针是最重要的,它指向了真实存储字符串内容的堆内存,容量是堆内存分配空间的大小,长度是目前已经使用的大小。
- 从上面的描述中我们可以了解到,对于堆中长度可变的值,Rust 通过栈上固定长度的“胖指针”实现了对堆上值的所有权。
- 从代码角度理解一下第二条,
let s1 = String::from("hello"); let s2 = s1;
前面一句代码,会再堆上分配一块空间存储”hello”,并在栈上分配一个胖指针,它存储有指向堆中空间的堆地址、字符串长度、字符串容量。第二句中,会在栈上创建一个胖指针,胖指针 s1 的内容(堆地址、长度、容量)会被拷贝到 s2 上,堆中的”hello”并不会被拷贝。第二句执行后,s1 便不再拥有堆中内容”hello”的所有权,任何通过 s1 访问堆中内容的尝试都会被 Rust 编译器拒绝。通过这种方式,Rust 可以保证堆中空间有且仅有一个变量引用,从而解决了堆中内存回收的问题。
- 注:可能会有人注意到,
let s1 = s;
与上面的let y = x;
的语义不太一样呢?在 Rust 中,固定长度的类型,包括整型、字符型等,甚至数组[T; n],都是固定长度的,它们存储在栈内存中;而非固定长度类型,例如 String、Vec等,都是存储在堆上的。对于前者,它们是实现了 Copy trait 的,而后者则没有。原因是,如果后者实现了,会违背有且仅有一个所有者的原则。
- 当胖指针变量离开了它的作用域,它指向的堆内存会被回收。这样 Rust 就不用像 Java 一样,还需要借助 GC 来回收堆内存;也避免了像 C/C++ 一样需要人为地回收内存,从而提高了安全性。
02-Move 语义
let s1 = String::from("hello");
let s2 = s1;
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}
fn main() {
let s = String::from("hello");
let s1 = takes_and_gives_back(s);
}
边栏推荐
- 为什么这么多人都想当产品经理?
- 五星控股汪建国:以“植物精神”深耕赛道,用“动物精神”推动成长
- [Microservice~Nacos] Configuration Center of Nacos
- 5个 Istio 访问外部服务流量控制最常用的例子,你知道几个?
- Pagoda measurement - building LightPicture open source map bed system
- 6个规则去净化你的代码
- TF中random.normal()与random.truncated_normal()
- 技术分享 | 接口自动化测试之JSON Schema模式该如何使用?
- Several ways to draw timeline diagrams
- mysql multi-table left link query
猜你喜欢
第十七期八股文巴拉巴拉说(数据库篇)
Flask之路由(app.route)详解
APP自动化测试框架-UiAutomator2基础入门
Xiaohei's leetcode journey: 94. Inorder traversal of binary trees (supplementary Morris inorder traversal)
APP automation test framework - UiAutomator2 introductory
Several ways to draw timeline diagrams
Domestic mobile phone manufacturers once fought for it, but now it is the first to collapse...
ACM MM 2022 | Cloud2Sketch: Painting with clouds in the sky, AI brush strokes
MLOps的演进历程
Flask's routing (app.route) detailed
随机推荐
One Pass 2074: [21CSPJ Popularization Group] Candy
Reinforcement Learning Weekly Issue 57: DL-DRL, FedDRL & Deep VULMAN
leetcode 刷题日记 计算右侧小于当前元素的个数
Pagoda measurement - building LightPicture open source map bed system
unit test
Technology Sharing | How to use the JSON Schema mode of interface automation testing?
js十五道面试题(含答案)
论文解读(DropEdge)《DropEdge: Towards Deep Graph Convolutional Networks on Node Classification》
mysql multi-table left link query
一文让你快速了解隐式类型转换【整型提升】!
腾讯继续挥舞降本增效“大刀”,外包员工免费餐饮福利被砍了
好未来,想成为第二个新东方
APP自动化测试框架-UiAutomator2基础入门
UML类图五种关系的代码实现[通俗易懂]
Space not freed after TRUNCATE table
Deceptive Dice
Basic operations of openGauss database (super detailed)
In programming languages, the difference between remainder and modulo
阿里云架构师金云龙:基于云XR平台的视觉计算应用部署
Synchronization lock synchronized traces the source