前言
rust中析构用Drop
trait
析构≠释放内存,一般析构里放释放内存操作
析构函数里面没有释放内存操作时可以重复调用,释放内存不行->double free
先定义2个结构体,其中MyStruct
包含InnerPtr
,InnerPtr
包含一个智能指针等待释放
struct MyStruct {
pub inner: InnerPtr,
}
impl MyStruct {
fn new() -> Self {
Self { inner: InnerPtr::new() }
}
}
impl Drop for MyStruct {
fn drop(&mut self) {
println!("calling drop for MyStruct @ {:p}", self);
}
}
#[derive(new)]
struct InnerPtr {
#[new(default)]
data: Box<u8>,
}
impl Drop for InnerPtr {
fn drop(&mut self) {
println!("calling drop for InnerPtr, data @ {:p}", self.data.as_mut());
}
}
一、栈
forget
阻止对象脱离作用域后自动析构。drop
提前析构对象,可用于提前释放锁
fn main() {
let a = MyStruct::new();
println!("{:p}", &a);
println!("{:p}", &a.inner);
println!("{:p}", &a.inner.data);
// drop(a);
mem::forget(a);
}
ManuallyDrop
自己负责何时调用析构
fn main() {
let a = MyStruct::new();
let mut manual_a = ManuallyDrop::new(a);
unsafe { ManuallyDrop::drop(&mut manual_a); }
}
二、堆
1.智能指针
如果只想要数据的指针而不想手动管理内存可以下面这样搞,用指针而不是引用的好处在于不用担心a的所有权发生转移导致指针不可用
fn main() {
let mut a = Box::new(MyStruct::new());
// 只获取对象指针 智能指针还在
let p = a.as_mut() as *mut MyStruct;
let mut b = a;
println!("{:p}", p);
println!("{:p}", b.as_mut());
}
智能指针转换为裸指针需要手动释放内存,由图可知需要释放2块内存,其中智能指针在析构函数里面实现了释放内存,因此最简单的方法如下:drop_in_place(p)
递归调用自身和字段的析构函数释放结构体内部分配的内存,dealloc(p ...)
释放MyStruct内存
fn main() {
let mut a = Box::new(MyStruct::new());
// 智能指针->裸指针
let p = Box::into_raw(a);
unsafe {
// 调用析构
drop_in_place(p);
// 释放内存
dealloc(p as *mut u8, Layout::new::<MyStruct>());
}
}
如果知道所有内存的位置也可以全用dealloc
fn main() {
let mut a = Box::new(MyStruct::new());
// 智能指针->裸指针
let p = Box::into_raw(a);
println!("{}", size_of::<MyStruct>());
unsafe {
// 释放内存
dealloc((*p).inner.data.as_mut() as *mut u8, Layout::new::<u8>());
dealloc(p as *mut u8, Layout::new::<MyStruct>());
}
}
或者专门调用二级智能指针的drop,再释放MyStruct
fn main() {
let mut a = Box::new(MyStruct::new());
// 智能指针->裸指针
let p = Box::into_raw(a);
println!("{}", size_of::<MyStruct>());
unsafe {
// 调用析构
// Inner与MyStruct的内存地址相同
drop_in_place(p as *mut InnerPtr);
// drop_in_place(&mut ((*p).inner));
// 释放内存
dealloc(p as *mut u8, Layout::new::<MyStruct>());
}
}
2.分配器
alloc
分配未初始化内存,直接调用drop会尝试释放里面的智能指针导致STATUS_HEAP_CORRUPTION
,所以drop前要赋值。或者直接释放内存而不drop
fn main() {
// 创建布局, 申请内存
let layout = Layout::new::<MyStruct>();
unsafe {
let p = alloc(layout) as *mut MyStruct;
p.write(MyStruct::new());
// 调用对象的drop
drop_in_place(p);
// 释放内存
dealloc(p as *mut u8, layout);
}
}
分配数组方式如下,drop_in_place
输入*mut [T]
来析构数组所有元素,*mut [T]
是一个胖指针,里面有长度信息
fn main() {
let layout = Layout::array::<MyStruct>(3).unwrap();
unsafe {
let p = alloc(layout) as *mut MyStruct;
p.write(MyStruct::new());
p.add(1).write(MyStruct::new());
p.add(2).write(MyStruct::new());
// 调用对象的drop
drop_in_place(slice_from_raw_parts_mut(p, 3));
// 释放内存
dealloc(p as *mut u8, layout);
}
}