不带自动内存回收(Garbage Collection)的内存安全是Rust语言最重要的创新,是它与其他语言最主要的区别所在,是Rust语言设计的核心。

Rust希望通过语言的机制和编译器的功能,把程序员易犯错、不易检查的问题解决在编译期,避免运行时的内存错误。

1. rust的内存管理

采用虚拟内存空间在栈和堆上分配内存,这是诸多编程语言通用的内存管理基石,Rust当然也不例外。然而,与C/C++语言不同的是,Rust不需要开发者显式地通过malloc/new或free/delete 之类的函数去分配和回收堆内存。

栈内存的生命周期是短暂的,会随着栈展开(常见的是函数调用)的过程而被自动清理。而堆内存是动态的,其分配和重新分配并不遵循某个固定的模式,所以需要使用指针来对其进行跟踪。

Rust受现代C++的启发,同样引入了智能指针来管理堆内存。智能指针在堆上开辟内存空间,并拥有其所有权,通过存储于栈中的指针来管理堆内存。智能指针的 RAII 机制利用栈的特点,在栈元素被自动清空时自动调用析构函数,来释放智能指针所管理的堆内存空间。

函数的局部变量

在函数中定义的局部变量都会被默认存储到栈中。这和C/C++语言,甚至更多的语言行为都一样,但不同的是,Rust编译器可以检查未初始化的变量,以保证内存安全。

Rust编译器会对代码做基本的静态分支流程分析。

当函数调用完毕时,栈帧会被释放,局部变量会被清空。如果变量指向堆内存,那么Rust会自动清空其指向的已分配堆内存。这就是智能指针。

Rust中的指针

Rust中的指针大致可以分为三种:引用、原生指针(裸指针)和智能指针。

  • 原生指针可以在 unsafe 块下任意使用,不受 Rust 的安全检查规则的限制
  • 引用则必须受到编译器安全检查规则的限制。
  • 智能指针是对指针的一层封装,提供了一些额外的功能,比如自动释放堆内存。
    智能指针区别于常规结构体的特性在于,它实现了Deref和Drop 这两个trait(可理解为接口)。Deref提供了解引用能力,Drop提供了自动析构的能力,正是这两个trait让智能指针拥有了类似指针的行为。比如String和Vec类型就是一种智能指针(和C++中的String和Vector很相似)。

RAII(构造和析构)

RAII的机制来源于C++,RAII的设计目标就是替代GC,防止内存泄漏。RAII使用构造函数来初始化资源,使用析构函数来回收资源。

RAII和GC最大的不同在于,RAII将资源托管给创建堆内存的指针对象本身来管理,并保证资源在其生命周期内始终有效,一旦生命周期终止,资源马上会被回收。

GC是由第三方只针对内存来统一回收垃圾的,这样就很被动。

Rc引用计数智能指针

注意,在多个指针指向同一块内存的情况下,RAII机制是无法避免内存泄露的。这个时候Rust提供了智能指针Rc<T>,它的名字叫引用计数(referencecounting)智能指针。Rc<T>内部维护着一个引用计数器,每clone一次,计数器加1,当它们离开main函数作用域时,计数器会被清零,对应的堆内存也会被自动释放。

2. rust的内存安全

内存不安全的例子

  • 空指针
    解引用空指针是不安全的。这块地址空间一般是受保护的,对空指针解引用在大部分平台上会产生segfault。
  • 野指针
    野指针指的是未初始化的指针。它的值取决于它这个位置以前遗留下来的是什么值。所以它可能指向任意一个地方。对它解引用,可能会造成segfault,也可能不会,纯粹凭运气。但无论如何,这个行为都不会是你预期内的行为,是一定会产生bug的。
  • 悬空指针
    悬空指针指的是内存空间在被释放了之后,继续使用。它跟野指针类似,同样会读写已经不属于这个指针的内容。
  • 使用未初始化内存
    不只是指针类型,任何一种类型不初始化就直接使用都是危险的,造成的后果我们完全无法预测。
  • 非法释放内存
    分配和释放要配对。如果对同一个指针释放两次,会制造出内存错误。如果指针并不是内存分配器返回的值,对其执行释放操作,也是危险的。
  • 缓冲区溢出
    指针访问越界了,结果也是类似于野指针,会读取或者修改临近内存空间的值,造成危险。

Rust是如何解决内存安全问题的?

  • 使用未定义内存
    Rust中的变量必须初始化以后才可使用,否则无法通过编译器检查。
  • 空指针
    开发者没有任何办法去创建一个空指针。Rust中使用Option类型来代替空指针,Option实际是枚举体,包含两个值:Some(T)和None,分别代表两种情况,有和无。这就迫使开发者必须对这两种情况都做处理,以保证内存安全。
  • 悬空指针
    悬空指针指的是内存空间在被释放了之后,继续使用。Rust通过所有权和借用机制解决这个问题。
  • 缓冲区溢出
    Rust编译器在编译期就能检查出数组越界的问题,从而完美地避免了缓冲区溢出。
  • 非法释放未分配的指针或已经释放过的指针。
    Rust中不会出现未分配的指针,所以也不存在非法释放的情况。同时,Rust的所有权机制严格地保证了析构函数只会调用一次,所以也不会出现非法释放已释放内存的情况。

3. 所有权系统

现代C++的RAII机制解决了无GC自动管理内存的基本问题,但并没有解决全部问题,还存在着很多安全隐患。

空指针解引用

#include <iostream>
#include <memory>
using namespace std;
int main(){unique_ptr<int> orig(new int(5));cout << *orig << endl;auto stolen = move(orig);cout << *orig << endl;
}

上面这段C++代码,使用了move函数,将原来的unique_ptr指针赋予了stolen,并转让了所有权。原来的 orig 则变为了空指针,而对空指针解引用是很不安全的,所以该C++代码运行时就会抛出段错误(segmentationfault)。

fn main(){let orig = Box::new(5);println!("{}", *orig);let stolen = orig;println!("{}", *orig);
}

这段Rust代码和上面的C++代码作用一样,但是在Rust中,错误会在编译期间爆出来。

Rust中的所有权是系统性概念

可以看到Rust并没有显式地使用任何类似现代C++中的move函数来转移所有权,却拥有和现代C++一样的效果。

现代C++中的RAII机制虽然也有所有权的概念,但其作用范围非常有限,仅智能指针有所有权,并且现代C++编译器也并没有依据所有权进行严格检查,所以才会出现代码那样的解引用空指针的运行时错误。

而在Rust中,所有权是系统性的概念,是Rust语言中的基础设施。Rust中的每个值都必定有一个唯一控制者,即,所有者。所有权的转移都是按系统性的规则隐式地自动完成的,这也是代码清单5-2如此简洁的原因。

所有权系统是Rust的核心,可以说掌握了所有权系统,就等于掌握了Rust。

所有权机制

Rust中分配的每块内存都有其所有者,所有者负责该内存的释放和读写权限,并且每次每个值只能有唯一的所有者。这就是 Rust的所有权机制(OwnerShip)。

转移所有权

  • 对于可以安全地在栈上进行按位复制的类型,就只需要按位复制,也方便管理内存。

  • 对于在堆上存储的数据,因为无法安全地在栈上进行按位复制,如果要保证内存安全,就必须进行深度复制。深度复制需要在堆内存中重新开辟空间,这会带来更多的性能开销。如果堆上的数据不变,只需要在栈上移动指向堆内存的指针地址,不仅保证了内存安全,还可以拥有与栈复制同样的性能。(这就叫转移所有权)

引用与所有权借用

引用(Reference)是 Rust 提供的一种指针语义。引用是基于指针的实现,它与指针的区别是,指针保存的是其指向内存的地址,而引用可以看作某块内存的别名(Alias),使用它需要满足编译器的各种安全检查规则。

引用也分为不可变引用和可变引用。使用&符号进行不可变引用,使用&mut符号进行可变引用。

在所有权系统中,引用&x也可称为x的借用(Borrowing),通过&操作符来完成所有权租借。既然是借用所有权,那么引用并不会造成绑定变量所有权的转移。但是借用所有权会让所有者(owner)受到如下限制:

  • 在不可变借用期间,所有者不能修改资源,并且也不能再进行可变借用。
  • 在可变借用期间,所有者不能访问资源,并且也不能再出借所有权。

引用在离开作用域之时,就是其归还所有权之时。使用借用,与直接使用拥有所有权的值一样自然,而且还不需要转移所有权。

智能指针与所有权转移

智能指针和普通引用的区别之一就是所有权的不同。智能指针拥有资源的所有权,而普通引用只是对所有权的借用。

对于智能指针Box<T>类型来说,如果包含的类型T属于复制语义,则执行按位复制;如果属于移动语义,则移动所有权。

进一步学习Rust

欢迎加入我的知识星球,我会提供一些学习资料(书籍/视频)以及解答一些问题。
大家一起学习Rust

Rust是如何实现内存安全的--理解RAII/所有权机制/智能指针/引用相关推荐

  1. C++ 智能指针std::shared_ptr简单使用和理解

    参考:https://blog.csdn.net/u011068702/article/details/83692838 1  智能指针std::shared_ptr相关知识和如何使用 我们这里先说下 ...

  2. C++11 解决内存泄露问题的智能指针:shared_ptr、unique_ptr、weak_ptr

    我们经常听到内存泄漏,但是对这个抽象的概念一直没有什么理解,比如产生内存泄漏又将如何,我平时写程序从来不考虑这个等等.这篇的目的:第一,给大家实验实验内存泄露带来的问题,让大家直观感受内存泄露.第二, ...

  3. C++智能指针:更简单、更高效的内存管理方法

    C++智能指针:从新手到高手的心理密码C++ Smart Pointers: Psychological Passcodes from Beginner to Expert 智能指针简介 (Intro ...

  4. C++相关:动态内存和智能指针

    前言 在C++中,动态内存的管理是通过运算符new和delete来完成的.但使用动态内存很容易出现问题,因为确保在正确的时间释放内存是及其困难的.有时候我们会忘记内存的的释放,这种情况下就会产生内存泄 ...

  5. C++ 智能指针 :内存泄漏、 RAII、智能指针、auto_ptr、unique_ptr、shared_ptr、weak_ptr、定制删除器deleter

    文章目录 内存泄漏 什么是内存泄漏 内存泄漏的危害: 如何避免内存泄漏 RAII 智能指针 auto_ptr unique_ptr shared_ptr 循环引用问题 weak_ptr 定制删除器 内 ...

  6. 2.5w字长文爆肝 C++动态内存与智能指针一篇搞懂!太顶了!!!

    动态内存与智能指针 1.动态内存与智能指针 2.shared_ptr类 2.1.make_shared函数 2.2.shared_ptr的拷贝和赋值 2.3.shared_ptr自动销毁所管理的对象 ...

  7. C++ 以智能指针管理内存资源

    1.简介 C++ 作为一门应用广泛的高级编程语言,却没有像 Java.C# 等语言拥有垃圾回收(Garbage Collection )机制来自动进行内存管理,这也是C++ 一直被诟病的一点.C++ ...

  8. 32. 对c++中的smart pointer四个智能指针shared_ptr,unique_ptr,weak_ptr,auto_ptr的理解

    C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被11弃用. 智能指针的作用是管理一个指针, ...

  9. 使用智能指针错误导致内存泄漏_C++智能指针使用的那些事

    指针指针的由来 在C/C++里面,内存管理由开发者自己管理.指针变量总是指向一片内存空间,这片内存空间可以是局部变量.也可以是通过malloc.new申请的.如果申请的内存没有释放,就会导致内存泄漏. ...

最新文章

  1. 子div在父div中置底
  2. 文件,文件夹的创建和删除
  3. matlab图像降噪_图像超分:RealSR
  4. mybatis 取查询值_Oracle结合Mybatis实现取表TOP 10条数据
  5. 队列消息在html中怎么排列,Redis实现消息队列
  6. set python_set在python里的含义和用法
  7. 手工画图和计算机画图的内在联系,工程制图与计算机绘图教案10-11-1
  8. 如何删除双系统中的ubuntu系统
  9. python实用【大技巧】之Python手把手实现远程控制桌面
  10. 从无序到有序的负熵力量
  11. iapp将音量调至最大
  12. 白天不去搜寻痛苦,但晚上却不能抑制
  13. 从零开始学IDA逆向(百度云)
  14. maya 白天室内灯光_Maya课时:白天的灯光构建视频教程_翼狐网
  15. 【原创】【I2C】I2C介绍
  16. Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask
  17. 你知道如何健康饮水吗
  18. 信息安全 SEED Lab8 Cross-Site Scripting (XSS) Attack Lab
  19. 记录java围棋小游戏心得
  20. 计算机一级实验素材题目,计算机一级EXCEL操作题整理素材(12页)-原创力文档...

热门文章

  1. python argparse_Python 命令行之旅——初探 argparse
  2. deephash项目代码使用指北
  3. Python机器学习:评价分类结果008ROC曲线
  4. c语言编写图形登录窗口,「分享」C语言如何编写图形界面
  5. android8 通知呼吸灯_手机呼吸灯那么好用!为什么要取消它?没有呼吸灯真的好吗?...
  6. 计算机计算资源估计,项目时间管理中的计算题三点估算和关键路径法
  7. cpython vm_【协程原理】 - cPython的VM真变态
  8. mysql geometry 附近的人_mysql中geometry类型的简单使用(搜索附近的人)
  9. python 批量打印文档_使用python将Excel数据填充Word模板并生成Word
  10. 西部数码 php.ini,如何修改PHP的memory_limit限制