Skip to content

智能指针

BOX\<T>

能够在堆上存储数据

Box使用

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

Rust需要在编译时确定类型占用的内存空间, 通常的类型不能处理递归类型(无法确定将要占用的内存空间)。 而由于Box是指针,所以它能处理递归类型

处理递归类型

use crate::List::{Cons, Nil};

fn main() {
    let list = Cons(1,Cons(2,Cons(3,Nil)));
}


enum List { // 报错
    Cons(i32,List),
    Nil,
}
error[E0072]: recursive type `List` has infinite size
 --> src/main.rs:8:1
  |
8 | enum List {
  | ^^^^^^^^^
9 |     Cons(i32,List),
  |              ---- recursive without indirection
  |
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
  |
9 |     Cons(i32,Box<List>),
  |              ++++    +

修改后:

use crate::List::{Cons, Nil};

fn main() {
    let list = Cons(1,
        Box::new(Cons(2,
            Box::new(Cons(3,
                Box::new(Nil))))));
}


enum List {
    Cons(i32,Box<List>),
    Nil,
}

解引用

fn main() {
    let x = 5;
    let y = Box::new(x);
    assert_eq!(5, x);
    assert_eq!(5, *y);
}

实现自己的Box类型

use std::ops::Deref;

struct MyBox<T>(T);

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

impl<T> Deref for MyBox<T> { // 实现deref
    type Target = T;

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

fn main() {
    let x = 5;
    let y = Box::new(x);
    assert_eq!(5, x);
    assert_eq!(5, *y);
}

Drop Trait

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("{}" , self.data)
    }
}

fn main() {
    let c = CustomSmartPointer { data: String::from("test") };
    // c.drop(); // not allowed
    let d = CustomSmartPointer { data: String::from("test2") };
    drop(d);
}
test2
test

类似于C++中的析构函数

Rust不允许手动调用drop方法 但可以调用std::mem::drop

Rc

引用计数的智能指针(只能用于单线程环境)

Rc<\T>

共享只读数据

use std::rc::Rc;
use crate::List::{Cons,Nil};

enum List {
    Cons(i32,Rc<List>),
    Nil
}


fn main() {
    let a = Rc::new(Cons(5,Rc::new(Cons(10,Rc::new(Nil)))));
    println!("count = {}", Rc::strong_count(&a));

    a.clone(); // 类型clone 深拷贝  而Rc::clone是浅拷贝 只增加引用计数
    println!("count = {}", Rc::strong_count(&a));

    let b = Cons(3, Rc::clone(&a)); // a的引用计数 + 1
    println!("count = {}", Rc::strong_count(&a));

    {
        let c = Cons(4, Rc::clone(&a));
        println!("count = {}", Rc::strong_count(&a));
    }
    println!("count = {}", Rc::strong_count(&a));

}
count = 1
count = 1
count = 2
count = 3
count = 2

RefCell 和 内部可变性

Rust中的RefCell<T>是一个智能指针,它允许您在运行时借用可变或不可变的数据。RefCell<T>被设计为在运行时检查借用规则,而不是在编译时。

RefCell<T>的使用通常涉及到两种方法:.borrow().borrow_mut().borrow()用于获取一个不可变引用,而.borrow_mut()用于获取一个可变引用。在运行时,RefCell<T>会跟踪当前的借用情况,并在违反规则时引发运行时错误。

以下是一个简单的示例,展示了如何在RefCell<T>中存储一个可变的字符串,并进行读取和修改:

use std::cell::RefCell;

fn main() {
    let my_string = RefCell::new(String::from("hello"));

    let borrowed_string = my_string.borrow();
    println!("{}", borrowed_string);

    let mut mutable_string = my_string.borrow_mut();
    mutable_string.push_str(", world!");
    println!("{}", mutable_string);
}

在此示例中,我们首先创建了一个名为my_stringRefCell<T>,其中包含了一个可变的字符串。然后,我们使用.borrow()方法获取了一个不可变的引用,并打印了字符串。接下来,我们使用.borrow_mut()方法获取了一个可变的引用,并将其修改为带有新值的字符串,然后再次打印出来。

另一个示例是在循环中使用RefCell<T>来构建一个树形结构。在这个例子中,我们使用RefCell<T>来在节点之间传递可变引用:

use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>,
}

fn main() {
    let leaf1 = Node {
        value: 1,
        left: None,
        right: None,
    };
    let leaf2 = Node {
        value: 2,
        left: None,
        right: None,
    };
    let node1 = Node {
        value: 3,
        left: Some(Box::new(leaf1)),
        right: Some(Box::new(leaf2)),
    };
    let leaf3 = Node {
        value: 4,
        left: None,
        right: None,
    };
    let leaf4 = Node {
        value: 5,
        left: None,
        right: None,
    };
    let node2 = Node {
        value: 6,
        left: Some(Box::new(leaf3)),
        right: Some(Box::new(leaf4)),
    };
    let root = Node {
        value: 7,
        left: Some(Box::new(node1)),
        right: Some(Box::new(node2)),
    };
    let tree = RefCell::new(root);

    inorder_traversal(&tree.borrow());
}

fn inorder_traversal(node: &Node) {
    if let Some(left) = &node.left {
        inorder_traversal(left);
    }
    println!("{}", node.value);
    if let Some(right) = &node.right {
        inorder_traversal(right);
    }
}

在此示例中,我们构建了一个二叉树,并使用RefCell<T>来在节点之间传递可变引用。我们还定义了一个名为inorder_traversal()的函数,该函数使用递归方式遍历二叉树,并按顺序打印出每个节点的值。

use std::{rc::Rc, cell::{RefCell, Ref}};
use crate::List::{Cons,Nil};

#[derive(Debug)]
enum List {
    Cons(Rc<RefCell<i32>>,Rc<List>),
    Nil
}


fn main() {
    let value = Rc::new(RefCell::new(5));
    let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
    let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
    let c =Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));

    *value.borrow_mut() += 10;
    println!("a after = {:?}",a);
    println!("b after = {:?}",b);
    println!("c after = {:?}",c);

}

output

a after = Cons(RefCell { value: 15 }, Nil)
b after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))
c after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))

防止循环引用把Rc<\T>换为Weak<\T>