rust中实现全局可变结构

rust不允许同时存在对同一个值的多个可变引用。那么如何实现全局可变结构呢?

1. idea

考虑如下方法:我们仅使用对该值不可变引用,这相当于告知rust编译器我们只会读取该值,这样我们就可获得多个不可变引用。但实际上,在需要修改该值时,我们会通过unsafe进行类型转换,将不可变引用转换为可变引用。这样我们就相当于获得了多个可变引用。

通过unsafe的转换类似下面的函数:

1
2
3
4
// This function must never exist
fn make_mut<T>(value: &T) -> &mut T {
    unsafe { /* magic */ }
}

此时需要注意到的是,我们已经违反了rust对引用施加的规则,即at most one mutable reference at a time规则。

为了维持该规则同时达到我们的需要:我们需要动态维持该规则,即在运行时保证同一时间仅有一个可变引用。结果是,我们得以共享对该结构的引用(通过&引用),同时在需要时安全地获取可变引用(通过上述&T -> &mut T的动态borrow checking函数)。

上面所述的idea有多种具体实现。

1.1. 实现一:使用锁

1
2
3
4
5
6
7
fn lock<T>(value: &T) -> Locked<&mut T> {
    unsafe { lock(value); cast value to Locked<&mut T> }
}

impl Drop for Locked<&mut T> {
    fn drop(&mut self) { unlock(self.value) }
}

这种实现即标准库中的Mutex

1.2. 实现二:当违反规则时终止程序

1
2
3
4
5
6
7
8
9
10
11
fn get_mut<T>(value: &T) -> Mut<&mut T> {
    unsafe {
        if ref_count(value) != 0 { panic!() }
        ref_count(value) += 1;
        cast value to Mut<&mut T>
    }
}

impl Drop for Mut<&mut T> {
    fn drop(&mut self) { ref_count(value) -= 1; }
}

这种实现即RefCell::borrow_mut()

1.3. 实现三:仅当规则满足时返回可变引用

1
2
3
4
5
6
7
8
9
10
11
12
13
fn get_mut<T>(value: &T) -> Option<Mut<&mut T>> {
    unsafe {
        if ref_count(value) != 0 { None }
        else {
            ref_countvalue) += 1;
            Some(cast value to Mut<&mut T>)
        }
    }
}

impl Drop for Mut<&mut T> {
    fn drop(&mut self) { ref_count(value) -= 1; }
}

这种实现即RefCell::try_borrow_mut()

2. 小结

上述实现方法本质上都是 “interior mutability” 的某种实现。

3. 参考