在 Rust 中使用 slice::from_raw_parts_mut 和 ptr::drop_in_place 进行内存释放

Memory deallocation using slice::from_raw_parts_mut and ptr::drop_in_place in rust

我在网上看到一段代码,它使用 std::slice::from_raw_parts_mut()std::ptr::drop_in_place() 的组合来删除分配的内存。下面是一段代码,它分配了一个包含十个整数的数组,然后将其取消分配:

use std::{
    alloc::{alloc, Layout},
    ptr::NonNull,
};

fn main() {
    let len: usize = 10;
    let layout: Layout = Layout::array::<i32>(len).unwrap();
    let data: NonNull<i32> = unsafe { NonNull::new(alloc(layout) as *mut i32).unwrap() };

    unsafe {
        std::ptr::drop_in_place(std::slice::from_raw_parts_mut(data.as_ptr(), len));
    }
}

std::slice::from_raw_parts_mut()的return类型是可变切片&mut [T],但std::ptr::drop_in_place()的参数是*mut T。在我看来,转换是自动发生的。我很确定我在这里遗漏了一些东西,因为它不应该被允许。有人能解释一下这里到底发生了什么吗?

当您编写 std::slice::from_raw_parts_mut(data.as_ptr(), len) 时,您正在构建一个类型为 &mut [i32] 的值。

然后你将它传递给 drop_in_place() 或多或少定义为:

fn drop_in_place<T: ?Sized>(to_drop: *mut T)

所以你将 &mut [i32] 强制转换为 *mut T,分两步解决:reference to pointer 自动强制转换,然后 T解析为 [i32] 这是 drop 实际被调用的类型。

(你可能认为从引用到指针的自动强制转换是危险的,不应该是自动的,但实际上它是完全安全的。不安全的通常是你之后对指针所做的事情。实际上有一个一些安全的原始指针的使用,例如 std::ptr::eqstd::ptr::hash).

切片通过简单地遍历元素并在每个元素中调用 drop_in_place 来实现 Drop::drop。这是避免手动编写循环的聪明方法。

但请注意有关此代码的几件事:

  1. drop_in_place 将在切片的每个元素上调用 Drop::drop,但由于它们属于 i32 类型,因此实际上是空操作。我猜你的原始代码使用了泛型。
  2. drop_in_place 不会释放内存,为此您需要调用 std::alloc::dealloc.