从C数组指针在Rust中创建Vec并安全地释放它? [英] Creating a Vec in Rust from a C array pointer and safely freeing it?

查看:124
本文介绍了从C数组指针在Rust中创建Vec并安全地释放它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm calling a C function from Rust which takes a null pointer as as an argument, then allocates some memory to point it to.

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

What is the correct way to efficiently (i.e. avoiding unnecessary copies) and safely (i.e. avoid memory leaks or segfaults) turn data from the C pointer into a 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()
}

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

If I understand correctly, .to_vec() will copy data from the slice into a new Vec, so the underlying memory will still need to be freed (as the underlying memory for the slice won't be freed when it's dropped).

解决上述问题的正确方法是什么?

What is the correct approach for dealing with the above?

  • 我是否可以创建一个Vec来获取基础内存的所有权,而当Vec被释放时,该内存将被释放?
  • 如果没有,我应该在Rust中的哪里/如何释放C函数分配的内存?
  • 以上任何其他可以/应该改进的地方?
  • can I create a Vec which takes ownership of the underlying memory, which is freed when the Vec is freed?
  • if not, where/how in Rust should I free the memory that the C function allocated?
  • anything else in the above that could/should be improved on?

推荐答案

我可以创建一个Vec来获取基础内存的所有权吗?

can I create a Vec which takes ownership of the underlying memory, which is freed when the Vec is freed?

不安全,不.除非指针最初来自Vec(好吧,来自同一内存分配器),否则您绝对不能使用Vec::from_raw_parts.否则,您将尝试释放分配器不知道的内存.一个非常糟糕的主意.

Not safely, no. You must not use Vec::from_raw_parts unless the pointer came from a Vec originally (well, from the same memory allocator). Otherwise, you will try to free memory that your allocator doesn't know about; a very bad idea.

请注意,String::from_raw_parts也是一样,因为StringVec<u8>的包装.

Note that the same thing is true for String::from_raw_parts, as a String is a wrapper for a Vec<u8>.

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

where/how in Rust should I free the memory that the C function allocated?

一旦完成,就不会很快.

As soon as you are done with it and no sooner.

上述其他可以/应该改进的地方吗?

anything else in the above that could/should be improved on?

  • 调用slice::from_raw_parts
  • 时无需强制转换指针
  • 不需要在变量上使用显式类型
  • 使用ptr::null,而不是ptr::null_mut
  • 执行NULL指针检查
  • 检查长度是否为负数
    • There's no need to cast the pointer when calling slice::from_raw_parts
    • There's no need for explicit types on the variables
    • Use ptr::null, not ptr::null_mut
    • Perform a NULL pointer check
    • Check the length is non-negative
    • 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() {}
      


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


      You didn't state why you need it to be a Vec, but if you never need to change the size, you can create your own type that can be dereferenced as a slice and drops the data when appropriate:

      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() {}
      

      另请参阅:

      • How to convert a *const pointer into a Vec to correctly drop it?
      • Is it possible to call a Rust function taking a Vec from C?

      这篇关于从C数组指针在Rust中创建Vec并安全地释放它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆