【Rust 中级教程】 08 所有权(1)

简介: 【Rust 中级教程】 08 所有权(1)

0x00 开篇


从今天起开启 Rust 的重头戏,前面的知识可能对于一些拥有编程基础的读者们来说很简单,因为基本上都大同小异,但是所有权这个是 Rust 的新知识点。本篇文章将告诉你 Rust 为什么安全以及介绍一些通用的概念。本篇文章的阅读时间大约 6 分钟。


0x01 Rust 为什么安全


为了保证安全性,Rust 有两个承诺:

  • 开发者决定程序中值的生存时长。
    Rust 将会在你的控制下迅速释放与某个值关联的内容和其它资源。
  • 开发者将永远不会在一个对象释放后还使用指向它的指针。
    不会出现悬垂指针的问题。在程序编译期,Rust 将会捕获到这些错误。

悬垂指针:指针所指向的对象已经被释放或者回收了,但是指向该对象的指针没有作任何的修改,仍旧指向已经回收的内存地址。此类指针称为垂悬指针。

那么其它语言是如何处理的呢?


C/C++


C/C++ 仅仅是遵循了第一个原则,所以你常会在 C/C++ 中遇到悬垂指针。说实话,遇到程序崩溃算是运气不错,悬垂指针很容易出现重大的安全漏洞问题。所以在使用 C/C++ 时,我们要非常细心。


Java/C#


很多高级语言为了实现第二个原则,引入了垃圾回收机制。垃圾回收机制是当指向某个对象的所有指针都失效时会自动释放该对象。所以这些语言没有遵循第一个原则,对象释放的控制权交由了垃圾回收器(其实也可以主动调用回收,开发者一般不会主动调用)。这种做法的优点就是开发者在使用时可以专注业务,这也是 Java/Go 流行的原因之一了。

但是 Rust 不接受任何一项妥协,它打破了常规,另寻出路,采用了一种特别的方式——限制程序使用指针的方式。这也是为什么网络上充斥着各种以下好笑的言论:Rust 学习周期陡峭学习 Rust 要跟编译器作斗争 等等吧。其实这都是为了安全。在介绍所有权前,我们先来了解几个语言里的通用概念。


0x02 深复制与浅复制


上篇文章,我们了解了栈和堆。而深拷贝和浅拷贝就与栈和堆有关系。

  • 浅复制:也称作浅拷贝。是指复制栈上的数据,所以有时也称作栈复制。
    在 Rust 中,基本的数据类型都支持浅复制。通俗点说,数据只存储在栈上的数据类型都默认支持浅复制。
    示例代码:
fn main() {
    // 1. 值语义
    let a = 8;
    let mut b = a;
    b = 10;
    println!("a = {}, b = {}", a, b);
}
  • 0a2653c851af460fa595bd959398a8f1.png
  • 深复制:也称作深拷贝。是指复制栈上的数据和堆上的数据。
    在 Rust 中,字符串类型,数组,向量,胖指针等在栈上和堆上都存在数据的类型,我们需要进行深复制。

  • 2d65d23f6d4748949b924e4057485923.png

0x03 值语义(Value Semantics)


值语义,又叫值类型语义(Value-Type Semantics)或者按值复制语义(Copy-By-Value Semantics)。值语义是指按位复制后与原值无关,保证值的独立性,唯一性。在大部分语言中的基本数据类型都是值语义。按位复制就是栈复制。通俗一点讲,具有值语义的对象只能由赋值运算符修改。我们可以根据需要复制它,且任何副本都可以代替原始副本,而不会改变程序的行为。对于一个对象,只有对象的值是重要的,它的标识并不重要。


0x04 引用语义(Reference Semantics)


与值语义相对应的是引用语义。引用语义一般是指数据存储在堆内存中,通过栈内存里的指针来管理内存的数据(网络上找到的定义)。以 Rust 的向量(我们以及在《RUST 学习日记 第12课 ——切片(Slice)》介绍过向量的存储方式)为例,声明向量时,有一个区域是指向堆区的指针,而真正的数据则来自指针指向堆区的数据。可以按位复制(浅复制),但是按位复制会导致一个栈上会有多个指针指向堆上的同一区域,释放指针时容易造成堆上的数据二次释放。如果要复制引用语义上的数据通常是采用深复制,将栈上和堆上的数据同时复制。


0x05 所有权机制


上面两个语义,基本所有的语言都会存在。在此基础上,Rust 增加了另外两个语义——复制语义(Copy Semantics)和移动语义(Move Semantics)。这算是 Rust 对值语义与引用语义的重新包装。复制语义对应值语义,移动语义对应引用语义。但是在所有权的机制下,又存在一些不同。

  • 复制语义:可以在栈上进行安全的按位复制的类型。
  • 移动语义:对于在堆上存储的数据,在栈上按位复制数据时,会导致多指针指向同一堆上的数据,容易引发内存问题。所以要解决这个问题,我们必须使用深复制。但是深复制时,需要开闭新的内存空间,又带了性能问题。所以我们可以保持堆上的数据不变,我们只需要移动指向堆内存的指针即可。

0x06 小结


本篇文章主要介绍了一些概念吧,有些读者也许看完文章会感觉到很懵,Rust 提出的移动语义也许你在其他任何语言中都没有见过。接下来的文章也将会告诉你为什么 Rust 要提出移动语义的概念。 

相关文章
|
5月前
|
存储 Rust
【Rust】——所有权规则、内存分配
【Rust】——所有权规则、内存分配
|
2月前
|
存储 Rust 安全
30天拿下Rust之所有权
在编程语言的世界中,Rust凭借其独特的所有权机制脱颖而出,为开发者提供了一种新颖而强大的工具来防止内存错误。这一特性不仅确保了代码的安全性,还极大地提升了程序的性能。在Rust中,所有权是一种编译时检查机制,用于追踪哪些内存或资源何时可以被释放。每当一个变量被赋予一个值(比如:字符串、数组或文件句柄)时,Rust会确定这个变量是否“拥有”这个值,拥有资源的变量负责在适当的时候释放这些资源。
35 5
|
3月前
|
存储 Rust 安全
【Rust学习】04_所有权
所有权是 Rust 最独特的特性,对语言的其余部分有着深远的影响。它使 Rust 能够在不需要垃圾收集器的情况下保证内存安全,因此了解所有权的运作方式非常重要。在本章中,我们将讨论所有权以及几个相关功能:借用、切片以及 Rust 如何在内存中布局数据。
21 1
|
5月前
|
存储 Rust 安全
Rust 笔记:Rust 语言中的 所有权 与 生命周期
Rust 笔记:Rust 语言中的 所有权 与 生命周期
178 0
|
4月前
|
Rust 安全 开发者
Rust引用、借用和所有权详解
Rust引用、借用和所有权详解
|
5月前
|
Rust 算法 安全
【Rust中的所有权系统深入解析】A Deep Dive into Rust‘s Ownership System
【Rust中的所有权系统深入解析】A Deep Dive into Rust‘s Ownership System
89 0
|
5月前
|
Rust 安全 编译器
深入Rust的所有权系统:理解变量的所有权
本文详细探讨了Rust编程语言中所有权系统的核心概念,包括变量的所有权、生命周期、借用规则和内存安全。通过理解这些概念,我们能够编写出更加高效、安全和可维护的Rust代码。
|
5月前
|
Rust 编译器
【Rust】——函数(所有权)以及借用或引用
【Rust】——函数(所有权)以及借用或引用
|
5月前
|
存储 缓存 Rust
【Rust】——所有权:Stack(栈内存)vs Heap(堆内存)(重点)
【Rust】——所有权:Stack(栈内存)vs Heap(堆内存)(重点)
|
5月前
|
存储 Rust 安全
Rust核心功能之一(所有权)
Rust核心功能之一(所有权)