[rust]RefCell智能指针

9 阅读2分钟

定义

在Rust中,默认情况下所有的数据是不可变的,RefCell 作为Rust标准库中的一种智能指针类型,通过在运行时而非编译时执行借用检查,从而提供内部可变性,即在拥有不可变引用的同时允许对其中的数据进行修改。这在一定程度上打破了 Rust 的所有权和借用规则,使得在一些特定场景下编写更灵活和简洁的代码成为可能。

由于借用检查是在运行时进行的,会有一些额外的开销。因此在性能敏感的场景下,使用 RefCell 需要谨慎。

如果不小心违反了借用规则,程序会在运行时崩溃而不是在编译时发现错误。因此在使用 RefCell 时应尽量避免复杂的借用逻辑。

使用场景

复杂数据结构

实现树,图,链表等复杂数据结构时,多个节点间可能需要共享并修改同一个数据

#[derive(Debug)]
struct Node {
    value: i32,
    next: Option<Rc<RefCell<Node>>>,
}

use std::cell::RefCell;
fn main() {
    let node1 = Rc::new(RefCell::new(Node { value: 1, next: None }));
    let node2 = Rc::new(RefCell::new(Node { value: 2, next: Some(Rc::clone(&node1)) }));
    let node3 = Rc::new(RefCell::new(Node { value: 3, next: Some(Rc::clone(&node1)) }));

    println!("[before] Node1 value: {:?}", node1);
    println!("[before] Node2 value: {:?}", node2);
    println!("[before] Node3 value: {:?}", node3);

    println!("****************************************");
    node1.borrow_mut().value += 10;

    println!("[after] Node1 value: {:?}", node1);
    println!("[after] Node2 value: {:?}", node2);
    println!("[after] Node3 value: {:?}", node3);
}

// 输出:
// [before] Node1 value: RefCell { value: Node { value: 1, next: None } }
// [before] Node2 value: RefCell { value: Node { value: 2, next: Some(RefCell { value: Node { value: 1, next: None } }) } }
// [before] Node3 value: RefCell { value: Node { value: 3, next: Some(RefCell { value: Node { value: 1, next: None } }) } }
// ****************************************
// [after] Node1 value: RefCell { value: Node { value: 11, next: None } }
// [after] Node2 value: RefCell { value: Node { value: 2, next: Some(RefCell { value: Node { value: 11, next: None } }) } }
// [after] Node3 value: RefCell { value: Node { value: 3, next: Some(RefCell { value: Node { value: 11, next: None } }) } }

单线程共享可变数据

非常适合需要在多个地方共享和修改数据的单线程环境中使用,且也只能在单线程环境中使用

use std::cell::RefCell;
fn main() {
    let data = RefCell::new(5);

    {
        let mut data_ref = data.borrow_mut();
        *data_ref += 1;
        println!("Modified data: {}", data_ref); // 6
    }

    {
        let data_ref = data.borrow();
        println!("Data: {}", data_ref); // 6
    }
}

当你想在不转移所有权的情况下借用某个值时,可以使用 borrow 方法

Rc结合使用

Rc提供了不可变的多所有权,结合RefCell可以提供对数据的可变共享

use std::cell::RefCell;
fn main() {
    let data = Rc::new(RefCell::new(5));

    let clone1 = Rc::clone(&data);
    let clone2 = Rc::clone(&data);

    {
        let mut data_ref = clone1.borrow_mut();
        *data_ref += 1;
    }

    {
        let data_ref = clone2.borrow();
        println!("Data: {}", data_ref); // 6
    }
}