Rust初学者大多会遇到这样的问题:

  • 为什么同一资源不可被同时可变借用和不可变借用?
  • 为什么Rc一定只能是只读的,一定要配合std::cell系列(Cell,RefCell,UnsafeCell)才能提供可变性?为什么不设计一个可变的Rc?
  • 为什么Mutex/RwLock一般都会配合Arc使用?

    ……

这一类借用规则的问题,实际上都可以通过另一种思路来很好地解答,当然这也是很多大佬一定程度上认可的理解方式:

Rust中的借用,分为不可变借用共享借用,和可变借用独占借用

无论在教程中,还是语法中,我们都用可变性来区分二种借用,最开始,最直观的理解:

  • 因为是可变借用,多方借用会导致冲突,因此可变借用是独占的。
  • 因为是不可变借用,多方借用只读数据不会导致冲突,因此不可变借用是可共享的。

但是我们可以试着换一个方向去思考,用独占性去区分两种借用,很多问题便可以轻易理解:

  • 因为是独占借用,对资源的任何形式的访问都只能有一方可以被允许,所以对它的改变是安全无害的。
  • 因为是共享借用,对资源的访问可以有多方共同进行,所以对其中任何一方而言,来自其他方的改变是不可控的,所以对它的改变并不是安全无害的,因此它只允许只读访问。

这样的思考方式从“可变性==>独占性”换成了“独占性==>可变性”,首先就直观地解释了“同一资源不可同时被可变借用和不可变借用”的问题,因为“独占借用”从字面上就不可能跟其他任何形式的借用共存。

再说到Rc/Arc。Rc是通过引用计数来提供安全的共享访问的只能指针,在C++中本就叫做"shared_ptr",从逻辑上就是提供共享访问的工具,不应被任何一方独占,因此也理应只提供共享借用。而Arc就是线程安全版的Rc。

无论是std::cell系列还是Mutex/RwLock,它们的目的都一致:为共享资源提供安全的可变访问。由于既需要共享,也需要可变,因此无法用“独占=可变,共享=不可变”的类型模型去编译期检查访问的安全性,只能在运行时通过实际的行为去动态检查,保证“同一时间,一个资源只可以被一方可变访问,或被多方不可变访问”的原则。只是它们的行为略有不同:

  • UnsafeCell仅仅是一层包装壳,在对本身的访问不需要所有权或独占借用,仅提供共享借用的前提下,提供对内含资源的独占引用访问,因为没有任何策略保证访问的安全,因此对其的操作是unsafe的。
  • Cell屏蔽掉了UnsafeCell的引用访问,只提供存取访问。由于在单线程下,调用函数即完全交出控制权,存取都可被视为原子操作,而资源本身也从一个静态的资源变成了一个需要操作去存取的读写对象,因此自然也保证了安全访问的原则。
  • RefCell在UnsafeCell的基础上,以性能为代价,加入了运行时检查机制以提供安全的引用访问。由于引用可以在调用结束后持续地被持有,因此需要guard(通过自身的RAII来控制目标资源的占用与解除占用的结构)来运行时监控当前资源被访问的形式和数量,在打破安全访问的原则时及时抛出panic来避免ub。
  • RwLock是线程安全版的RefCell,通过调用系统底层的线程锁来实现线程安全,在打破安全访问原则时阻塞后续的访问而不是panic,以等待目前的占用解除。
  • Mutex是只读访问互斥版的RwLock,即使不存在可写访问,仍然会在已有只读访问占用时阻塞后续的只读访问。

不难看出,很多情况下,Arc<RwLock>的组合实际上跟Rc<RefCell>的逻辑是一致的:在需要共享的可变资源时,先用Rc/Arc控制资源的生命周期,再用RefCell/RwLock来控制资源的访问安全,区别在于是否为跨线程访问提供支持。

综上所述,以个人经验来讲,在习惯上以独占借用/共享借用的视角来分析,而不是可变借用/不可变借用,更有助于理解借用规则的实质,希望以此可以给初接触Rust的同行一些启发。

Rust借用机制的理解分析的更多相关文章

  1. Springboot学习04-默认错误页面加载机制源码分析

    Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...

  2. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  3. Redis数据持久化机制AOF原理分析一---转

    http://blog.csdn.net/acceptedxukai/article/details/18136903 http://blog.csdn.net/acceptedxukai/artic ...

  4. 浅谈 Attention 机制的理解

    什么是注意力机制? 注意力机制模仿了生物观察行为的内部过程,即一种将内部经验和外部感觉对齐从而增加部分区域的观察精细度的机制.例如人的视觉在处理一张图片时,会通过快速扫描全局图像,获得需要重点关注的目 ...

  5. [转]as3事件流机制彻底理解

    题记: 看过网上一些as3事件流的教程,觉得大多都讲得不甚清楚,让人不能直观的理解事件流.而这篇教程以将事件流过程比喻成捕鱼过程,形象简单. 在此基础上对于as3事件流总算有了全面的理解.事件流机制说 ...

  6. Linux内核OOM机制的详细分析(转)

    Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...

  7. 我对asp.net运行机制的理解

    有一本书叫<asp.net 本质论>,作者名叫冠军.我看了几次,都没有坚持看下来.这次连续看完了前三章,然后参考网上的资料,总结下我对.net运行机制的理解. 先上两张图: 看着这两张图, ...

  8. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...

  9. JDK动态代理深入理解分析并手写简易JDK动态代理(上)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-03/27.html 作者:夜月归途 出处:http://www.guitu ...

随机推荐

  1. Java基本数据类型、关键字

    Java基本数据类型.关键字 一.Java基本数据类型 Java有8中基本数据类型:byte\char \short \int\float\double\long\boolean 在说明每种类型大小前 ...

  2. Shell学习(四)Shell运算符

    一.基础运算 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用. expr 是一款表达式计算工具,使用它能完成表达式的求值操作. #计算两个数 ...

  3. 国产化之路-统信UOS操作系统安装

    专题目录 国产化之路-统信UOS操作系统安装 国产化之路-国产操作系统安装.net core 3.1 sdk 国产化之路-安装WEB服务器 国产化之路-安装达梦DM8数据库 国产化之路-统信UOS + ...

  4. 微信App支付接入步骤&支付中前后端交互流程

    最近对微信App支付(App端集成微信支付SDK)申请步骤,以及终端在进行微信支付时商户App.商户Server.微信App.微信支付Server的交互流程进行了简单了解.这篇文章应该算是学习笔记,分 ...

  5. 基于mockito做有效的单元测试

    概述 本文讲解的主要是有效和单元的思想,并不是说如何编写单元测试,用于改善和提高开发效率.编码风格.编码可读性和单测效率,不盲目追求覆盖率. 背景 现在很多单元测试只是利用@Test注解把代码或者整个 ...

  6. VARCHART XGantt如何计算截止日期

    甘特图从1998年的第一个商用版本开始就致力于计划编制和项目管理方面控件的研究和开发,经过20多年的积累和沉淀,目前可为软件开发商和最终用户提供最顶级的计划编制和项目管理的控件产品,帮助用户快速的整合 ...

  7. JS实现动态显示时间(最简单方法)

    使用JS实现动态显示时间 最简单实现方法 直接在网页适当的位置中插入如下js代码,(id="datetime") 不可省略. <div id="datetime&q ...

  8. 018 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 12 数据类型转换的基本概念

    018 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 12 数据类型转换的基本概念 本文知识点:Java中的数据类型转换 类型转换 类型转换分类 2类,分别是: ...

  9. Java知识系统回顾整理01基础01第一个程序05Eclipse中运行Java程序

    一.打开Java文件 直接打开在 命令行Hello World 中创建的java 文件 HelloWorld.java 二.运行 点击绿色运行按钮,直接运行 在eclipse中,编译过程自动执行了 三 ...

  10. Java知识系统回顾整理01基础05控制流程07结束外部循环

    一.break是结束当前循环 二.结束当前循环实例 break; 只能结束当前循环 public class HelloWorld { public static void main(String[] ...