RefCell

Rust在编译阶段会进行严格的借用规则检查,规则如下:

  • 在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用。
  • 引用必须总是有效。

即在编译阶段,当有一个不可变值时,不能可变的借用它。如下代码所示:

fn main() {
let x = 5;
let y = &mut x;
}

会产生编译错误:

error[E0596]: cannot borrow immutable local variable `x` as mutable
--> src/main.rs:32:18
|
31 | let x = 5;
| - consider changing this to `mut x`
32 | let y = &mut x;
| ^ cannot borrow mutably

但是在实际的编程场景中可能会需要在有不可变引用时改变数据的情况,这时可以考虑Rust中的内部可变性。其借用规则检查由编译期推迟到运行期。对应的,在编译期借用规则检查不通过,则会产生编译错误;而运行期借用规则检查不通过,则会panic,且有运行期的代价。

所以实际代码中使用RefCell<T>的情况是当你确定你的代码遵循借用规则,而编译器不能理解和确定的时候。代码仍然要符合借用规则,只不过规则检查放到了运行期。

RefCell代码实例1:

use std::cell::RefCell;

fn main() {
let x = RefCell::new(5u8);
assert_eq!(5, *x.borrow());
{
let mut y = x.borrow_mut();
*y = 10;
assert_eq!(10, *x.borrow());
let z = x.borrow(); //编译时会通过,但运行时panic!
}
}

运行结果:

thread 'main' panicked at 'already mutably borrowed: BorrowError', libcore/result.rs:983
:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

可以看到在运行时进行了借用检查,并且panic!

RefCell代码实例2:

#[derive(Debug, Default)]
struct Data {
a: u8,
b: RefCell<u8>,
} impl Data {
// 编译通过
pub fn value_b(&self) -> u8 {
let mut cache = self.b.borrow_mut();
if *cache != 0 {
return *cache;
}
*cache = 100;
*cache
} //编译错误:cannot mutably borrow field of immutable binding
pub fn value_a(&self) -> u8 {
if self.a != 0 {
return self.a;
} self.a = 100;
self.a
}
} fn main() {
let value = Data::default();
println!("{:?}", value);
value.value_b();
println!("{:?}", value);
}

value_a注释掉运行结果如下:

Data { a: 0, b: RefCell { value: 0 } }
Data { a: 0, b: RefCell { value: 100 } }

很多时候我们只能获取一个不可变引用,然而又需要改变所引用数据,这时用RefCell<T>是解决办法之一。

内部可变性

内部可变性(Interior mutability)是Rust中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许的。为此,该模式在数据结构中使用unsafe代码来模糊Rust通常的可变性和借用规则。当可以确保代码在运行时会遵守借用规则,即使编译器不能保证的情况,可以选择使用那些运用内部可变性模式的类型。所涉及的 unsafe 代码将被封装进安全的 API 中,而外部类型仍然是不可变的。

Rust中的RefCell和内部可变性的更多相关文章

  1. 【译】理解Rust中的Futures (一)

    原文标题:Understanding Futures In Rust -- Part 1 原文链接:https://www.viget.com/articles/understanding-futur ...

  2. 【译】Rust中的array、vector和slice

    原文链接:https://hashrust.com/blog/arrays-vectors-and-slices-in-rust/ 原文标题:Arrays, vectors and slices in ...

  3. 【译】理解Rust中的闭包

    原文标题:Understanding Closures in Rust 原文链接:https://medium.com/swlh/understanding-closures-in-rust-21f2 ...

  4. 【译】理解Rust中的Futures(二)

    原文标题:Understanding Futures in Rust -- Part 2 原文链接:https://www.viget.com/articles/understanding-futur ...

  5. 【译】深入理解Rust中的生命周期

    原文标题:Understanding Rust Lifetimes 原文链接:https://medium.com/nearprotocol/understanding-rust-lifetimes- ...

  6. Rust 中的继承与代码复用

    在学习Rust过程中突然想到怎么实现继承,特别是用于代码复用的继承,于是在网上查了查,发现不是那么简单的. C++的继承 首先看看c++中是如何做的. 例如要做一个场景结点的Node类和一个Sprit ...

  7. Rust 中的类型转换

    1. as 运算符 as 运算符有点像 C 中的强制类型转换,区别在于,它只能用于原始类型(i32 .i64 .f32 . f64 . u8 . u32 . char 等类型),并且它是安全的. 例 ...

  8. Rust中的Slices

    这个slice切片,python中有,go中有, 但确实,Rust中最严格. 精彩见如下URL: Rust 程序设计语言(第二版) 简体中文版 · GitBook (Legacy) https://k ...

  9. 刷完欧拉计划中难度系数为5%的所有63道题,我学会了Rust中的哪些知识点?

    我为什么学Rust? 2019年6月18日,Facebook发布了数字货币Libra的技术白皮书,我也第一时间体验了一下它的智能合约编程语言MOVE,发现这个MOVE是用Rust编写的,看来想准确理解 ...

随机推荐

  1. 【从刷面试题到构建知识体系】Java底层-synchronized锁-2偏向锁篇

    上一篇通过构建金字塔结构,来从不同的角度,由浅入深的对synchronized关键字做了介绍, 快速跳转:https://www.cnblogs.com/xyang/p/11631866.html 本 ...

  2. SpringBoot整合MybatisPlus3.X之乐观锁(十三)

    主要适用场景 意图: 当要更新一条记录的时候,希望这条记录没有被别人更新 乐观锁实现方式: 取出记录时,获取当前version 更新时,带上这个version 执行更新时, set version = ...

  3. 如何判断float值有效

    // 一个浮点数是否有效,首先要看其是否是一个数字(_isnan为0),其次还要看其是否超出了表示范围(_finite为0) // 注意_finite是有限的意思 #include <float ...

  4. vue设置页面标题

    使用vue-wechat-title插件对页面标题进行设置 1.安装模块    命令行窗口中运行npm install vue-wechat-title --save PS.如果程序正在运行,ctrl ...

  5. 哪种方式更适合在React中获取数据?

    作者:Dmitri Pavlutin 译者:小维FE 原文:dmitripavlutin.com 国外文章,笔者采用意译的方式,以保证文章的可读性. 当执行像数据获取这样的I/O操作时,你必须发起获取 ...

  6. 翻遍互联网都找不到的解决方案,一行代码轻松实现 Gitbook 默认折叠左侧菜单效果

    Gitbook 是一款产品文档构建工具,也可以用于构建个人博客,默认情况下电脑端访问时左侧菜单是展开状态,可偏偏有人想要实现默认折叠效果,于是诞生了这篇文章! 善良的我选择帮助别人 可能是网上关于 G ...

  7. 区块链之Hyperledger(超级账本)Fabric v1.0 的环境搭建(超详细教程)

    https://blog.csdn.net/so5418418/article/details/78355868

  8. Ubuntu13.10编译android源码中遇到的问题

    1. jdk的版本不对 我开始安装的是最新的jdk7,但是编译时会出现jdk的版本

  9. 使用Typescript重构axios(二十四)——防御XSRF攻击

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  10. JQuery弹出菜单时禁止页面(body)滚动

    最近在做手机端的弹出菜单,但是菜单弹出来后滑动手机屏幕的话页面滚动总是会将菜单滑上去,体验非常不好,所以查了一下弹出菜单时禁止页面滚动的方法,整理如下: 方法一:弹出菜单时给body和html添加一个 ...