Rust从入门到暴走(25)--- Rust的智能指针

Rust 的智能指针为程序员提供了强大的内存管理和数据结构构建工具。通过 Box、Rc 和 Arc 等类型,可以实现所有权传递、共享所有权和多线程安全等功能。智能指针还通过 RefCell 和 Mutex 等提供了内部可变性和并发安全性。智能指针是 Rust 中重要的工具,有效地平衡了性能、安全性和灵活性,为开发者提供了可靠的解决方案。

1.Box<T>:堆上分配内存

Box<T> 允许将数据存储在堆上,并在离开作用域时自动释放。

fn main() {
    
    
    let x = Box::new(5); // 使用Box将整型数据5存储在堆上,并将x指向堆上的数据
    println!("x = {}", x); // 打印x的值
} // x超出作用域,Box的析构函数被调用,堆上的数据被自动释放

运行结果:

x = 5

2.Rc<T>:引用计数智能指针

Rc<T> 允许多个所有者共享相同的数据,使用引用计数进行内存管理。

use std::rc::Rc;

fn main() {
    
    
    let shared_data = Rc::new(vec![1, 2, 3]); // 创建一个引用计数智能指针,用于共享一个Vec
    let clone1 = Rc::clone(&shared_data); // 克隆引用计数智能指针
    let clone2 = shared_data.clone(); // 也可以使用方法调用语法进行克隆
    println!("{:?}", shared_data); // 打印共享数据
} // 共享数据在所有所有者离开作用域后才会被释放

运行结果:

[1, 2, 3]

3.Arc:多线程安全的引用计数智能指针

Arc 是 Rc 的多线程安全版本,使用原子操作进行引用计数,适用于多线程环境。

use std::sync::Arc;
use std::thread;

fn main() {
    
    
    let shared_data = Arc::new(vec![1, 2, 3]); // 创建一个多线程安全的引用计数智能指针
    let clone1 = Arc::clone(&shared_data); // 克隆智能指针
    let clone2 = Arc::clone(&shared_data); // 克隆智能指针
    let handle1 = thread::spawn(move || {
    
    
        println!("{:?}", clone1);
    });
    let handle2 = thread::spawn(move || {
    
    
        println!("{:?}", clone2);
    });
    handle1.join().unwrap(); // 等待线程1结束
    handle2.join().unwrap(); // 等待线程2结束
} // 共享数据在所有所有者离开作用域后才会被释放

运行结果:

[1, 2, 3]
[1, 2, 3]

4.Cell<T>和RefCell<T>:提供内部可变性的智能指针

两个都是提供内部可变性的智能指针,但两者有一定的区别。

(1)Cell<T>
Cell<T>:不进行运行时借用规则检查,因此在不可变引用的情况下可以修改其内部值,但无法创建可变引用。Cell<T> 适用于 T 实现 Copy 的情况:

use std::cell::Cell;

fn main() {
    
    
    let count = Cell::new(0); // 创建一个 Cell 包裹的可复制类型(Copy)整数

    // 使用不可变引用修改内部值
    count.set(1);
    println!("Count: {}", count.get()); // 输出: Count: 1

    // 无法创建可变引用,下面代码会导致编译错误
    // let mut borrowed_count = count.get_mut();
}

运行结果:

Count: 1

(2)RefCell<T>

RefCell和Cell功能一致,不过RefCell可用于你确信代码是正确的,而编译器却发生了误判时。在 Rust 编译器的ctxt结构体中,存在着许多被RefCell类型封装的map字段。这主要源于这些map在代码中被广泛地分散使用和修改。由于这种广泛的分散使用,管理可变和不可变状态变得异常复杂,有时甚至是不可行的。这种复杂性导致编译器经常会报各种错误,给开发者带来不便。

use std::cell::RefCell;

fn main() {
    
    
    let s = RefCell::new(String::from("hello, world"));
    let s1 = s.borrow();
    let s2 = s.borrow_mut();

    println!("{},{}", s1, s2);
}

上面代码在编译期不会报任何错误,你可以顺利运行程序,但是依然会因为违背了借用规则导致了运行期 panic。可以看到RefCell 实际上并没有解决可变引用和引用可以共存的问题,只是将报错从编译期推迟到运行时。

thread 'main' panicked at main.rs:413:16:
already borrowed: BorrowMutError
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

在不可变引用的情况下创建可变引用

use std::cell::RefCell;

fn main() {
    
    
    let data = RefCell::new(vec![1, 2, 3]); // 创建一个 RefCell 包裹的可变 Vec

    // 在不可变引用的情况下创建可变引用
    {
    
    
        let mut borrowed_data = data.borrow_mut();
        borrowed_data.push(5); // 可以在可变引用下再次修改内部值
    }

    // 获取修改后的值并打印
    println!("Data: {:?}", data.borrow()); // 输出: Data: [1, 2, 3, 4, 5]
}

运行结果:

Data: [1, 2, 3, 5]

猜你喜欢

转载自blog.csdn.net/qiangyipan1900/article/details/137457095