Published on

🦀 从 Rust 的引用看计算机的内存和数据安全

Authors
  • avatar
    Name
    阿森 Hansen
    Twitter
  • 阅读对象:计算机工程师,rust 程序员或爱好者
  • 预备知识:竞态条件和锁的基础知识,rust 中关于生命周期和引用与借用的基础知识

TL;DR 变量访问的三板斧

Rust 中访问变量有三种方式:

  1. 捕获所有权后访问
  2. 使用不可变引用访问
  3. 使用可变引用访问

1 安全而高效的 Rust

Rust 是一个讲究安全和高效的语言,我使用了 Rust 一段时间之后,逐渐领悟了 Rust 的作者对“安全”的理解。

对于 Rust 的初学者来说,最难的两道坎莫过于**“生命周期”“所有权”**这两个概念。这也是 Rust 语言最具特色最核心的概念,也正是它们保证了语言的安全和高效。

这篇文章写一写我自己对 Rust 所有权的理解。

2 Rust 的所有权

Rust 对变量维护所有权并管理它的引用,来保证内存安全

Rust 的变量声明非常简单:

let test_str = String::from("foobar");

这个语句做了三件事:

  1. 在内存中开辟一段空间。
  2. 将“foobar”字符串写入这段内存。
  3. 将该内存的所有权绑定到 test_str 变量

使用这个变量有三种方式:

// 1. 将字符串复制到新的内存空间
// 此时 test_str_copy 有了独立的所有权
let test_str_copy = test_str.clone();

// 2. 新建一个不可变引用(只读)
let test_str_ref = &test_str;

// 3. 创建一个可变引用(读写)
let test_str_mut_ref = &mut test_str;

以上方法是如何保证内存安全的呢?两个方面:

  • 一旦有个变量具有可变引用,那么这个变量的内存就无法在其他地方读写,从而保证了排他性
  • 如果一个变量具有不可变引用,那么就无法再被“可变引用”了,保证了在读内存的时候,值不会改变。

3 数据库的属性锁

Rust 所有权机制让我想到了另外一个概念:属性锁

属性锁在数据库和高并发编程里很常见。例如在数据库的事务管理中存在两种属性锁:

  • 共享锁(读锁):当一个事务对一个数据加上共享锁后,这个数据就不能被其他事务修改,但是其他的事务依然可以读该数据。也就是说,共享锁一个数据可以有多个,大家都可以读。
  • 排他锁(写锁):当一个事务要写一个数据时,会加排他锁,其他事务无法读或写这段数据。排他锁一个数据只能有一个,只有一个事务可以读写。

数据库中的“共享锁”和“排他锁”刚好对应了“不可变引用”和“可变引用”。

4 打个比方……

如果把 Rust 的一个变量或者数据库的一段数据比作一个妹子。

不可变引用就好比是单身的妹子,男孩子们都可以追。姑娘相当于被上了一个“共享锁”,大家都能和姑娘聊聊天但是不能动手。

可变引用就好像是已经有男朋友的妹子,像上了一把“排他锁”,只有她的男朋友可以牵手,其他男生都不能追求。

这样解释是不是形象了跟多?😂

心理学家说:爱情有“排他性”……

5 总结:计算机的内存安全

人类的大脑一次只能处理一件事,而计算机不一样,它可以并行处理很多事。就好像多重人格,计算机工程师必须想办法管理好计算机的“多重人格”,让他们能和谐相处。

当多重人格之间打架了,计算机程序就可能出错。为了保证不出错,工程师们会用“锁”来控制“多重人格”,让他们好好协作不出乱子。

这篇文章,我们看到了 Rust 用 “所有权” 来保证内存安全,而数据库用“属性锁”来实现表的数据安全。他们背后的原理和逻辑其实是一样的:让并行处理的过程有序而正确

参考

  1. 浅谈数据库共享锁与排它锁 - 知乎

  2. 引用与借用 - Rust语言圣经(Rust Course)