智能指针
上一个章节,我们已经见到了 原始指针 和 引用;也介绍了 Deref 和 Drop特征;
本节我们继续了解RUST其他智能指针类型
C语言,指针往往就是指针,不会存在这个指针和那个指针有什么区别,都是通过解引用的方式去使用指针;
指针所代表的值,由程序员自己去解释,你可以malloc 申请一个内存,然后把指针的值赋值给指针;你也可以
把变量的值 通过 &a的方式,把该指针值赋值给指针, 甚至可以 通过 int *a = &10; int **b = &a 把一个指针
地址在赋值给一个指针,这种行为在RUST中几乎就和原始指针一样
#[derive(Debug)]
struct Apple(i32);
fn main() {
let mut a = Apple(10);
let mut a_ptr1 = &mut a as *mut Apple;
let a_ptr_ptr = &mut a_ptr1 as *mut *mut Apple;
println!("{:p}",&a);
println!("{:p}",a_ptr1);
println!("{:p}",&a_ptr1);
println!("{:p}",a_ptr_ptr);
unsafe {
println!("{:?}",*(*a_ptr_ptr));
println!("{:?}",*a_ptr1);
}
}
原始指针一般不会直接使用,之前我们也看到了,原始指针的解引用必须要在unsafe模式执行,RUST不会再负责 指针的安全
引用已经是我们看到过的一个智能指针类型,而且我们也看到了引用使用的严苛性,这种严苛性保证引用使用指针是安全的
RUST 还提供了其他几种类型的智能指针,用于再不同场合下指针的使用,同时保证了内存安全
Box
Box智能指针几乎是最简单的智能指针了 Box智能指针主要实现:
- 实例是从堆上分配的(Box类型通过定义一个 Allocator内存分配器完成这件工作)
- Box指针符合RUST对于堆上内存安全的要求(释放时机、使用安全)
Box通过实现Drop特征,可以自动释放内存,同时会调用内部类型的Drop
struct Apple(i32);
impl Drop for Apple{
fn drop(&mut self) {
println!("drop apple");
}
}
fn main() {
let _apple = Box::new(Apple(10)); //等于堆上分配了一个初始化过的APPLE 结构体
} //当box变量所有权销毁,drop会释放堆上的内存 同时调用子类型的drop
Box通过实现Deref特征,使用它 就像使用一般的指针一样
#[derive(Debug)]
struct Apple(i32);
fn main() {
let mut apple_ptr = Box::new(Apple(10)); //等于堆上分配了一个初始化过的APPLE 结构体
println!("{:?}",*apple_ptr);
let apple_ptr_ref = &apple_ptr;
println!("{:?}",*apple_ptr_ref); // 回顾deref的引用归一
(*apple_ptr).0 = 100; // *(apple_ptr.derefmut()) 等于使用了 Apple的可变引用
println!("{:?}",*apple_ptr);
println!("{:?}",*apple_ptr_ref); // 回顾引用的使用规则
}
Box通过实现Deref特征, 保证对于变量的访问 符合引用的使用规则
#[derive(Debug)]
struct Apple(i32);
fn main() {
let mut apple_ptr = Box::new(Apple(10)); //等于堆上分配了一个初始化过的APPLE 结构体
let apple_ref: &Apple = &apple_ptr; // apple_ptr.deref() 等于使用了 Apple的 不可变引用
let mut_apple_ref: &mut Apple = &mut apple_ptr;// apple_ptr.derefmut() 等于使用了 Apple的 可变引用
// println!("{:?}",apple_ref ); //回顾引用使用约束
(*mut_apple_ref).0 = 100;
println!("{:?}",mut_apple_ref );
}
解引用移动?复制
#[derive(Debug)]
struct Apple(i32);
fn main() {
let mut apple_ptr = Box::new(Apple(10)); //等于堆上分配了一个初始化过的APPLE 结构体
let apple2 = *apple_ptr; //解引用赋值
println!("{:?}",apple_ptr);
}
解引用移动?复制
fn main() {
let mut int_ptr = Box::new(10); //等于堆上分配了一个初始化过的i32
let int2 = *int_ptr; //解引用赋值
println!("{:?}",int_ptr);
}
总结: 移动还是赋值,取决于T 的类型 是否实现了copy
Box支持所有权移动语义 虽然BOX肯能会放在栈里 但是它包裹着一个堆上的内存
fn main() {
let b = Box::new(10);
let a = b;
println!("{}",b);
}
智能指针其他特点
由于智能指针本身特点,一般会提供和其他指针的互转:
- Box可以使用
into_raw转为 原始指针,box被消耗(所有权被转移) - 由于原始指针不会被自动销毁,再使用完之后,需要再通过
from_raw得到新的BOX,由box自动销毁内存
pub fn into_raw(b: Box<T, A>) -> *mut T
fn main() {
let x = Box::new(String::from("Hello"));
let ptr: *mut String = Box::into_raw(x);
unsafe { println!("{}", *ptr) };
//如果不把指针再塞回去 会造成 内存泄漏
let _x = unsafe { Box::from_raw(ptr) };
}
练习Box实现简单的链表
链表支持 push pop,每一次都是再头部添加和挪出
struct List<T> {
head: Option<Box<Node<T>>>,
}
#[derive(Debug)]
struct Node<T> {
data: T,
next: Option<Box<Node<T>>>,
}
impl<T: std::fmt::Debug> List<T> {
fn new() -> List<T> {
List{head: None}
}
fn push(&mut self, data:T) {
let mut new_node = Node {data, next:None};
if self.head.is_none() {
//替换new node 为Head,head 挪动到newnode -> next
self.head = Some(Box::new(new_node))
} else {
//替换new node 为Head,head 挪动到newnode -> next
new_node.next = Some(self.head.take().unwrap());
self.head = Some(Box::new(new_node))
}
}
fn pop(&mut self) -> Option<T> {
let head = self.head.take()?;
let ans = Some(head.data);
self.head = head.next;
ans
}
fn show(&self) {
let mut index = 0;
let mut node_option_ref = self.head.as_ref();
loop {
if node_option_ref.is_some() {
let node_ref = node_option_ref.unwrap();
println!("index[{}] : {:?}", index, node_ref.data);
node_option_ref = node_ref.next.as_ref();
index+=1;
continue;
}
break;
}
}
}
#[derive(Debug)]
struct Apple(i32);
fn main() {
let mut list = List::new();
list.push(Apple(1));
list.push(Apple(2));
list.push(Apple(3));
list.show();
list.pop();
list.show();
list.pop();
list.pop();
list.show();
}