惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy & Cybersecurity Law Blog
SecWiki News
SecWiki News
T
Troy Hunt's Blog
Y
Y Combinator Blog
V
V2EX
美团技术团队
Last Week in AI
Last Week in AI
S
Security @ Cisco Blogs
IT之家
IT之家
博客园_首页
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
阮一峰的网络日志
阮一峰的网络日志
AI
AI
罗磊的独立博客
人人都是产品经理
人人都是产品经理
H
Hacker News: Front Page
N
News and Events Feed by Topic
P
Privacy International News Feed
V2EX - 技术
V2EX - 技术
Recent Commits to openclaw:main
Recent Commits to openclaw:main
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
GbyAI
GbyAI
L
LINUX DO - 热门话题
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Azure Blog
Microsoft Azure Blog
Martin Fowler
Martin Fowler
月光博客
月光博客
WordPress大学
WordPress大学
Latest news
Latest news
Google DeepMind News
Google DeepMind News
S
Schneier on Security
N
Netflix TechBlog - Medium
腾讯CDC
T
Tailwind CSS Blog
TaoSecurity Blog
TaoSecurity Blog
S
Secure Thoughts
L
LINUX DO - 最新话题
Project Zero
Project Zero
Cyberwarzone
Cyberwarzone
D
DataBreaches.Net
Webroot Blog
Webroot Blog
B
Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
H
Help Net Security
L
LangChain Blog
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

hsfzxjy 的博客

解决 VSCode + CMake + MSVC 编译器信息乱码的问题 使用 3090 部署 1.58bit 动态量化版 DeepSeek R1 671b 如何在 VS Code DevContainer 中配置 HTTP 代理 如何在跳板机背后的服务器上使用 VS Code Remote - Containers Cohesive Digests for Ints and Floats Rust 中的隐匿概念 —— Place(位置) 美术馆 一尺之槌,日取其半,1075日而竭 老生常谈:使用 Cloudflare 自选 IP 加速站点访问 辩义 State、Nation 与 Country 将 Base64 编码的数据快速转换为 Uint8Array 折腾 NPU·第1章 —— 搭建 Level Zero 开发环境 折腾 NPU·第0章 —— Intel NPU 概述与 Level-Zero 新增域名 monad.run CSS 中为特定字符设置不同字体 Arbitary Lifetime Transmutation via Rust Unsoundness Dijkstra 算法的延伸 Manacher 回文计数算法 硬卧 Go Fact: Zero-sized Field at the Rear of a Struct Has Non-zero Size Display *big.Rat Losslessly and Smartly in Golang 代码的仪式 Building Electron From Scratch 中式亲属称谓研究之一:构建半群 Some Notes on Kotlin Coroutines Git sparse-checkout and partial clones for Mega-Repos 辩义“封建” Diving from the CUDA Error 804 into a bug of libnvidia-container Modern Cryptography, GPG and Integration with Git(hub) Move the Root Partition of Ubuntu A New Programmer Kicks a Roadblock Git-based Dependencies in Dart and Go Reversy Naming 人类一败涂地 Invalid Golang Pointers Can Bite You Even If You Don't Dereference Side Project(副业) A Flaw of Promoting Complex Trait Bounds in Rust Initialize Process Pool Worker with Individual Value Rust - Python FFI From Scratch [Extending Hexo For My Site] Part 1 [Extending Hexo For My Site] Part 0 Debug a 'torch.tensor(1).cuda()' hanging 不自由的互联网 Retrieve Contents over HTTP without curl or wget [Unravelling mocona] Part 1 - Verbosity or Anti-Pattern [Unravelling mocona] Part 0 - Preface Understanding pickle in Python Rough Notes on Deploying Vaultwarden & NextCloud Bookmarks 语言狂热者与实用主义者 Demystify the randomness in CUDA kernels Performant Bulk Mutations in IndexedDB Auto Rebuild .pyx Files with pyximport Cython and Threads Obtain a Random Available TCP Port with Bash Information Theory: KL Divergence Information Theory: Entropy and Mutual Information 铁板烧 西郊线 Proof of the Gumbel Max Trick Option::as_ref Visualizing Correlation 三月十日杂感 三月一日杂感 二月十一日杂感 一月二十六日杂感 SS Configuration 一月七日杂感 四月·病 Haskell 笔记:State Monad Haskell 笔记:Monad 引论 Haskell 笔记:Applicative Haskell 笔记:Category Theory and Functor Haskell 笔记:data, type, newtype Haskell 笔记:folds 使用 Aria2 在 Ubuntu 中下载百度云资源 从伪并行的 Python 多线程说起 一个 Reentrant Error 引发的对 Python 信号机制的探索和思考 Linux 文件权限 HSFZMUN 4.0 部署小记 午后雨·科大 最后的雨夜·广州 揭秘·变态的平方根倒数算法 神坑·Python 装饰类无限递归 Python“黑魔法”之 Encoding & Decoding Ubuntu 重新映射键盘布局 为什么我要翻墙 Python“黑魔法”之 Generator Coroutines 数学美 之 判断线段相交的最简方法 除夕杂感 17 行代码实现的简易 Javascript 字符串模板 Python“黑魔法”之 Meta Classes 诗集 生活,需要被“发现” 家书·十八岁成人礼 炫技?还是需求? 【译】响应式图片的现状 【译】“为什么有这么多的编程语言?” Wisecity 商赛总结——也谈前端自动化测试 记一次 DoS 诈骗网站的经历 那一年,我们望向星空
Rc, RefCell and Interior Mutability
2019-06-23 · via hsfzxjy 的博客

Say we need a type Cursor<T> , which holds a mutable reference to T. A method .dup() duplicates the internal reference, wraps it in a new instance of Cursor<T> and returns. Such pattern exists commonly in database driver library. Users could hold multiple cursors simultaneously, with each owning a (mutable) reference to the same connection object.

One might implements with a primitive mutable reference:

struct Cursor<'a, T> {
obj: &'a mut T,
}

impl<'a, T> Cursor<'a, T> {
fn new(t: &'a mut T) -> Cursor<'a, T> {
Cursor { obj: t }
}

fn dup(&mut self) -> Cursor<T> {
Cursor { obj: self.obj }
}
}

fn main() {
let mut i = 1;
let mut cursor_a = Cursor::new(&mut i);
let _cursor_b = cursor_a.dup();
}

Perfect and neat, and luckily Rust compiler did not complain. Fresh Rustanceans would have to work hard for shutting up the compiler, especially when fighting with references.

The invocation of ::new() and .dup() are on separate lines. Now what about to chain up the constructor and .dup()? This time the compiler fails:

fn main() {
let mut i = 1;
let a = Cursor::new(&mut i).dup();
println!("{:?}", a.obj);
}
   |
21 | let a = Cursor::new(&mut i).dup();
| ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
22 | println!("{:?}", a.obj);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value

Wierd. Chaining invocations or not should make no difference, at least in most languages – but not in Rust.

To figure out the problem, let’s expand the signature of .dup() into a more verbose one:

fn dup<'a>(&'a mut self) -> Cursor<'a, T> {

}

Rust compiler allows us to elide the lifetime specifier 'a . The signature implies self should live at least as long as the returned value. However, by chaining the invocations, the code is in fact equivalent to:

let a = {
let mut __a = Cursor::new(&mut i);
__a.dup()
};

The temporary variable __a lives only within the block, and will be dropped at the end of it. But the return value of __a.dup() is passed out of the block, and therefore has a longer lifetime than __a , which violates the constraints. To fix it, we might instead implement a self-consuming version of .dup() , say .into_dup() :

    fn into_dup(self) -> Cursor<'a, T> {
Cursor { obj: self.obj }
}

.into_dup() moves all content of self into the new object, and makes no implication on the lifetime. Now we can write code like Cursor::new(&mut i).into_dup() .

Rc

The solution above is not satisfactory enough. Apparently we won’t make a self-consuming version for each method, just to please the compiler. A smart pointer with reference counting might be more suitable for the job. Let’s try to rewrite the code with std::rc::Rc :

use std::rc::Rc;

struct Cursor<T> {
obj: Rc<T>,
}

impl<T> Cursor<T> {
fn new(t: &Rc<T>) -> Cursor<T> {
Cursor { obj: t.clone() }
}

fn dup(&self) -> Cursor<T> {
Cursor {
obj: self.obj.clone(),
}
}
}

An Rc instance wraps a pointer (or reference) internally, and cloning the instance implicitly duplicates the pointer. But from the perspective of compiler, there’s no borrowing involved, and no more need for explicit lifetime declaration in struct and impl . We can now chain the invocations as expected:

fn main() {
let mut i = Rc::new(1);
let a = Cursor::new(&i).dup();
println!("i: {:?} a.obj: {:?}", a.obj, i);
}



Great! Looks like that Rc could help to relax the constraints of references, and allow us to share references more flexibly.

Now let’s move forward to another task – we attempt to mutate the target value through the reference:

fn main() {
let mut i = Rc::new(1);
let a = Cursor::new(&i).dup();

*Rc::get_mut(&mut i).unwrap() = 2;

println!("i: {:?} a.obj: {:?}", a.obj, i);
}

The highlighted line is much more verbose than expected. Rc does not implements DerefMut trait, so we could not obtain a mutable reference by mutable de-referencing. Instead, it provides Rc::get_mut() for the obj, which returns a value of type Optional<&mut T> . The Optional wrapper implies a possible failure during de-referencing. This could be confusing at the first sight – the object of type T is owned by the Rc<T> object, so how could the de-referencing be invalid?

We might defer the question in later paragraphs and go ahead. The code compiles smoothly, but this time we get a runtime panic:

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:347:21

Oops… So Rc::get_mut does return a None ! To explain this, we might go back to the docs for help:

The fact is that, Rc::get_mut help survive the compilation, but will perform a runtime check to ensure there’s no other Rc or Weak instances referencing the target, and it fails (returns None ) if so. This is claimed in the docs

  • Rc::get_mut Returns a mutable reference to the inner value, if there are no other Rc or Weak pointers to the same value.
  • Rc::get_mut Returns None otherwise, because it is not safe to mutate a shared value.

In the code above, we actually have two Rc instances, i and a.obj , sharing a same value internally. Rc::get_mut therefore denies the dereferencing. From this perspective, Rc resembles another borrow checker at runtime.

Here’s a brief comparison between Rc s and primitive references:

Rc :

  • At any moment there may exist several Rc<T> instances holding reference to the same internal value. One can use * to directly deference one of them to immutably access the internal value.
  • An Rc<T> instance can be mutably dereferenced if and only if it is the only instance holding reference to the internal value.

Primitive references:

  • For a value v of type T , at any moment there may exist several immutable reference to v . Each of them can be derefenced to obtain the target value.
  • An &mut T to v is legal, if and only if there is no other reference to v .

Sure, Rust is safe… But is our design incorrect? Not really. We may just misuse the underlying structure for the pattern.

The problem is, existence of an Rc<T> instance implies an “immutable reference” to the internal value, and thus disallows mutable access to it in the future. The implication drives out data races, but could be too aggressive for our scenario. In some cases, accessing and mutating an object will never collide. It would be better weaken the notion of “holding reference” from the long living time of Rc<T> to a much shorter one, for example, we could defer the checking to the time inner value being accessed, instead of the time Rc<T> being duplicated.

RefCell and Interior Mutability Pattern

But we would still need Rc , since it’s almost the only choice for reference sharing. The trick is to find some ways to mutate the inner value with only an immutable reference provided.

In Rust, the concept mutability is “infectious”. Even if we just attempt to partially mutate an object, a mutable reference to the whole object is still needed. It’s inconvenient for objects consisting of several independent parts 1. Such design does not play well with the borrow checker. From a more generic view, sometimes mutability of an object should be hidden, in order that it can be mutated with only immutable reference provided. This is what we called Interior Mutability Pattern in Rust.

The corresponding underlying structure is RefCell . A RefCell instance has .borrow() and .borrow_mut() method for immutable or mutable borrowing. Both of the two methods have &self in signature, and thus no requirement for compile-time mutability. For most of the time, they can be used as expected without any disturbing, but at the time the borrowing violate the rules, they would still panic.

Rc + RefCell

We could combine Rc and RefCell into Rc<RefCell<T>> and rewrite our program into expected behavior:

use std::cell::RefCell;
use std::rc::Rc;

struct Cursor<T> {
obj: Rc<RefCell<T>>,
}

impl<T> Cursor<T> {
fn new(t: &Rc<RefCell<T>>) -> Cursor<T> {
Cursor { obj: t.clone() }
}

fn dup(&self) -> Cursor<T> {
Cursor {
obj: self.obj.clone(),
}
}
}

fn main() {
let i = Rc::new(RefCell::new(1));
let a = Cursor::new(&i).dup();
*i.borrow_mut() = 2;
println!("i: {:?} a.obj: {:?}", a.obj, i);
}

The declaration is getting a little more verbose, but it does work! We can now share the reference between objects, and mutate the interval value via an immutable borrowing. With automatic dereference in Rust, the Rc layer becomes transparent. We can write statement like i.borrow_mut() to reduces most of the verbosity.

Now let’s see what would happen if multiple borrowings exist:

fn main() {
let i = Rc::new(RefCell::new(1));
let a = Cursor::new(&i).dup();

let _ref = i.borrow();

*i.borrow_mut() = 2;
println!("i: {:?} a.obj: {:?}", a.obj, i);
}

The program still compiles, but panicks at runtime:

thread 'main' panicked at 'already borrowed: BorrowMutError', src/libcore/result.rs:999:5

At the moment of i.borrow_mut() , another immutable borrowing _ref already exists and violates the rules. In a sense RefCell relaxes compile-time constraints and defers them to runtime. It makes a trade-off between compile-time safety and flexibility.

References


Author: hsfzxjy.
Link: .
License: CC BY-NC-ND 4.0.
All rights reserved by the author.
Commercial use of this post in any form is NOT permitted.
Non-commercial use of this post should be attributed with this block of text.