从C数组指针创建Rust中的Vec并安全地释放它?

Dav*_*lis 3 ffi rust

我正在从Rust调用一个C函数,它将一个空指针作为一个参数,然后分配一些内存来指向它.

什么是有效(即避免不必要的副本)和安全(即避免内存泄漏或段错误)的正确方法是将数据从C指针转换为Vec

我有类似的东西:

extern "C" {
    // C function that allocates an array of floats
    fn allocate_data(data_ptr: *mut *const f32, data_len: *mut i32);
}

fn get_vec() -> Vec<f32> {
    // C will set this to length of array it allocates
    let mut data_len: i32 = 0;

    // C will point this at the array it allocates
    let mut data_ptr: *const f32 = std::ptr::null_mut();

    unsafe { allocate_data(&mut data_ptr, &mut data_len) };

    let data_slice = unsafe { slice::from_raw_parts(data_ptr as *const f32, data_len as usize) };
    data_slice.to_vec()
}
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,.to_vec()将把切片中的数据复制到一个新的Vec,因此仍然需要释放底层内存(因为切片的底层内存在删除时不会被释放).

处理上述问题的正确方法是什么?

  • 我可以创建一个Vec取得底层内存的所有权,在释放时Vec释放它吗?
  • 如果没有,Rust中的哪个/怎么应该释放C函数分配的内存?
  • 以上可能/应该改进的其他内容?

She*_*ter 9

我可以创建一个Vec取得底层内存的所有权,在释放时Vec释放它吗?

不安全,没有.你不能使用Vec::from_raw_parts,除非指针从来到Vec最初(当然,从相同的内存分配器).否则,您将尝试释放分配器不知道的内存; 一个非常糟糕的主意.

我应该在哪里/如何在Rust中释放C函数分配的内存?

一旦你完成它并且不久就完成了.

以上可能/应该改进的其他内容?

  • 调用时无需转换指针 slice::from_raw_parts
  • 变量上不需要显式类型
  • 使用ptr::null,而不是ptr::null_mut
  • 执行NULL指针检查
  • 检查长度是否定的
use std::{ptr, slice};

extern "C" {
    fn allocate_data(data_ptr: *mut *const f32, data_len: *mut i32);
    fn deallocate_data(data_ptr: *const f32);
}

fn get_vec() -> Vec<f32> {
    let mut data_ptr = ptr::null();
    let mut data_len = 0;

    unsafe {
        allocate_data(&mut data_ptr, &mut data_len);
        assert!(!data_ptr.is_null());
        assert!(data_len >= 0);

        let v = slice::from_raw_parts(data_ptr, data_len as usize).to_vec();
        deallocate_data(data_ptr);

        v
    }
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)

您没有说明为什么需要它作为a Vec,但是如果您永远不需要更改大小,则可以创建自己的类型,可以将其作为切片解除引用并在适当时删除数据:

use std::{ptr, slice};

extern "C" {
    fn allocate_data(data_ptr: *mut *const f32, data_len: *mut i32);
    fn deallocate_data(data_ptr: *const f32);
}

struct CVec {
    ptr: *const f32,
    len: usize,
}

impl std::ops::Deref for CVec {
    type Target = [f32];

    fn deref(&self) -> &[f32] {
        unsafe { slice::from_raw_parts(self.ptr, self.len) }
    }
}

impl Drop for CVec {
    fn drop(&mut self) {
        unsafe { deallocate_data(self.ptr) };
    }
}

fn get_vec() -> CVec {
    let mut ptr = ptr::null();
    let mut len = 0;

    unsafe {
        allocate_data(&mut ptr, &mut len);
        assert!(!ptr.is_null());
        assert!(len >= 0);

        CVec {
            ptr,
            len: len as usize,
        }
    }
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  • *使用`libc :: free`是合适的* - \*shrug\*.它可能*工作; 当你从C使用那个函数时,你打电话来释放数据?这是正确的做法. (4认同)