Rust 中智能指针综合手册

智能指针是 Rust 内存安全保证和性能的基本组成部分。它们封装一个值并提供附加功能,确保内存管理高效且安全。本文深入探讨了 Rust 中可用的不同类型的智能指针、它们的工作原理以及何时使用它们。所有示例均与最新稳定版本 Rust 1.80.0 兼容。

什么是智能指针?
智能指针是一种数据结构,它不仅像指针一样工作,还具有额外的元数据和功能。与传统指针不同,Rust 中的智能指针可确保内存安全并可自动管理资源释放。它们是 Rust 所有权模型不可或缺的一部分。

主要特点

  1. 所有权和借用:智能指针遵守 Rust 的所有权和借用规则。
  2. 自动资源管理:当智能指针超出范围时,它们处理释放。
  3. 附加功能:它们提供额外的功能,例如引用计数和内部可变性。
  4. 类型安全:智能指针通常利用 Rust 的类型系统来防止常见的编程错误。

Box
Box是 Rust 中最简单的智能指针。它在堆上分配内存,并将所有权移至 Box。

何时使用 Box

  • 当你需要在堆上分配大量数据时。
  • 当您需要对堆分配的值拥有单独所有权时。
  • 当您需要定义递归数据结构时。
  • 当您想转移数据所有权而不复制数据时。


fn  main () { 
    let  b = Box :: new ( 5 ); 
    println! ( "b = {}" , b); 

    // 解除引用强制
    let  x = 5 ; 
    let  y = Box :: new (x); 
    assert_eq! ( 5 , x); 
    assert_eq! ( 5 , *y); 

    // 将 Box 用于递归类型
    enum  List { 
        Cons ( i32 , Box <List>), 
        Nil, 
    } 

    let  list = List:: Cons ( 1 , Box :: new (List:: Cons ( 2 , Box :: new (List::Nil)))); 
}

Rc 和 Arc
Rc和Arc都是引用计数的智能指针。Rc适用于单线程场景,而Arc(原子引用计数)适用于多线程场景。

何时使用 Rc

  • 当程序的多个部分需要读取相同的数据时。
  • 当单线程引用计数就足够时。
  • 在图形等数据结构中,多条边可能指向同一个节点。

use std::rc::Rc; 

fn  main () { 
    let  a = Rc:: new ( 5 ); 
    let  b = Rc:: clone (&a); 
    let  c = Rc:: clone (&a); 
    println! ( "a = {}, b = {}, c = {}" , a, b, c); 
    println! ( "引用计数:{}" , Rc:: strong_count (&a)); 
}


何时使用 Arc

  • 当您需要在线程之间共享数据时。
  • 在需要跨线程边界共享所有权的并发数据结构中。

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

fn main() {
    let a = Arc::new(5);
    let a1 = Arc::clone(&a);
    let a2 = Arc::clone(&a);

    let handle1 = thread::spawn(move || {
        println!("Thread 1: a1 = {}", a1);
    });

    let handle2 = thread::spawn(move || {
        println!("Thread 2: a2 = {}", a2);
    });

    println!("Main thread: a = {}", a);
    handle1.join().unwrap();
    handle2.join().unwrap();
}

RefCell 和内部可变性
RefCell允许在运行时进行可变借用,即使 RefCell 本身是不可变的。这种模式称为内部可变性。

何时使用 RefCell

  • 即使您的结构是不可变的,您也需要改变数据。
  • 当您想要在运行时而不是编译时强制执行借用规则时。
  • 在需要在只读上下文中修改数据的情况下。

use std::cell::RefCell;

fn main() {
    let x = RefCell::new(5);

    {
        let mut y = x.borrow_mut();
        *y += 10;
    }

    println!("x = {:?}", x);

    // Multiple borrows
    let z = x.borrow();
    let w = x.borrow();
    println!("z = {}, w = {}", z, w);

    
}

Weak
Weak 是与 Rc 或 Arc 一起使用的非所有引用。 它可以防止可能导致内存泄漏的循环引用。

use std::rc::{Rc, Weak};
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}

fn main() {
    let leaf = Rc::new(Node {
        value: 3,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![]),
    });

    let branch = Rc::new(Node {
        value: 5,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![Rc::clone(&leaf)]),
    });

    *leaf.parent.borrow_mut() = Rc::downgrade(&branch);

    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
    println!("branch strong count = {}, weak count = {}",
             Rc::strong_count(&branch),
             Rc::weak_count(&branch));
}

自定义智能指针
Rust 允许你通过实现 Deref 和 Drop 特性来创建自己的智能指针。 自定义智能指针可以根据特定需求提供额外的功能。

use std::ops::Deref;
use std::ops::Drop;

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> Drop for MyBox<T> {
    fn drop(&mut self) {
        println!("Dropping MyBox!");
    }
}

fn main() {
    let x = 5;
    let y = MyBox::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y);
}

实例和用例

1、Example: Building a Linked List

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

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

impl Node {
    fn new(value: i32) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Node {
            value,
            next: None,
        }))
    }
}

fn main() {
    let node1 = Node::new(1);
    let node2 = Node::new(2);
    let node3 = Node::new(3);

    node1.borrow_mut().next = Some(Rc::clone(&node2));
    node2.borrow_mut().next = Some(Rc::clone(&node3));

    println!("Linked List: {:?}", node1);
}

2、Example: Thread-Safe Shared State

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final count: {}", *counter.lock().unwrap());
}

结论
智能指针是 Rust 的一项强大功能,它提供了灵活而安全的内存管理。 了解不同类型的智能指针及其用例对于编写高效、安全的 Rust 代码至关重要。 无论您是使用 Box 管理堆内存、使用 Rc 或 Arc 共享数据,还是使用 RefCell 实现内部可变性,掌握智能指针都将大大提高您的 Rust 编程技能。

请记住,虽然智能指针提供了强大的功能,但它们也会带来一些开销。 在选择使用哪种智能指针时,请务必考虑程序的具体要求,当原始引用足以满足您的需求时,请毫不犹豫地使用原始引用。