Rust中的RefCell和内部可变性
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和内部可变性的更多相关文章
- 【译】理解Rust中的Futures (一)
原文标题:Understanding Futures In Rust -- Part 1 原文链接:https://www.viget.com/articles/understanding-futur ...
- 【译】Rust中的array、vector和slice
原文链接:https://hashrust.com/blog/arrays-vectors-and-slices-in-rust/ 原文标题:Arrays, vectors and slices in ...
- 【译】理解Rust中的闭包
原文标题:Understanding Closures in Rust 原文链接:https://medium.com/swlh/understanding-closures-in-rust-21f2 ...
- 【译】理解Rust中的Futures(二)
原文标题:Understanding Futures in Rust -- Part 2 原文链接:https://www.viget.com/articles/understanding-futur ...
- 【译】深入理解Rust中的生命周期
原文标题:Understanding Rust Lifetimes 原文链接:https://medium.com/nearprotocol/understanding-rust-lifetimes- ...
- Rust 中的继承与代码复用
在学习Rust过程中突然想到怎么实现继承,特别是用于代码复用的继承,于是在网上查了查,发现不是那么简单的. C++的继承 首先看看c++中是如何做的. 例如要做一个场景结点的Node类和一个Sprit ...
- Rust 中的类型转换
1. as 运算符 as 运算符有点像 C 中的强制类型转换,区别在于,它只能用于原始类型(i32 .i64 .f32 . f64 . u8 . u32 . char 等类型),并且它是安全的. 例 ...
- Rust中的Slices
这个slice切片,python中有,go中有, 但确实,Rust中最严格. 精彩见如下URL: Rust 程序设计语言(第二版) 简体中文版 · GitBook (Legacy) https://k ...
- 刷完欧拉计划中难度系数为5%的所有63道题,我学会了Rust中的哪些知识点?
我为什么学Rust? 2019年6月18日,Facebook发布了数字货币Libra的技术白皮书,我也第一时间体验了一下它的智能合约编程语言MOVE,发现这个MOVE是用Rust编写的,看来想准确理解 ...
随机推荐
- Docker配置整理
目录: 以非root用户身份管理Docker 配置Docker以在启动时启动 配置Docker守护程序监听连接的位置 以非root用户身份管理Docker 默认情况下,Docker运行权限由用户roo ...
- Oauth 2.0学习
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. 本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释,主要参考材料为R ...
- swift ARC中的strong、weak、unowned
Swift 用自动引用计数ARC(Automatic Reference Counting)方式来跟踪和管理app的内存使用.这使得内存管理成为swift内部的机制,不需要认为考虑.ARC会自动释放那 ...
- C和C++引用传递和数组传参引用
引用传递有两种传参方式,具体可参考文章 概括地讲,就是 *声明一个形参是指针,所以需要传递指针实参,对应的函数实现也应当遵循指针的语法.这种实现思路并不针对于C或者C++,因为它们都有指针,所以都可以 ...
- Java12 Collectors.teeing 你需要了解一下
前言 在 Java 12 里面有个非常好用但在官方 JEP 没有公布的功能,因为它只是 Collector 中的一个小改动,它的作用是 merge 两个 collector 的结果,这句话显得很抽象, ...
- 前端技术之:通过plop生成Controller的方法与步骤
# Controller的生成 开发者可以通过plop命令生成各种类型的控制器类(Normal.Restful.View), 以下是示例生成步骤. 1. 执行以下命令: plop controller ...
- 获取本机出口IP方法
Windows 浏览器常用方式访问: 1.http://www.ip138.com/ 2.http://tool.chinaz.com/ 3.在百度搜索框内输入 ip 会自动识别出来当前的出口IP ...
- linux 查看磁盘信息
一.查看磁盘和分区 ACCB947E:Home zhangsan$ df -h Filesystem Size Used Avail Capacity iused ifree %iused Mount ...
- Kong05-Kong 的健康检查和监控
您可以让 Kong 代理的 API 使用 ring-balancer , 通过添加包含一个或多个目标实体的upstream 实体来配置,每个目标指向不同的IP地址(或主机名)和端口.ring-bala ...
- [Luogu5384][Cnoi2019] 雪松果树
传送门 虽然这题是一道二合一,也不算难,但还是学到了很多东西啊,\(k\) 级儿子个数的五种求法!!我还是觉得四种比较好( \(k\) 级儿子个数有五种求法,你知道么? --鲁迅 首先 \(k\) 级 ...