rust - 从 C 数组指针在 Rust 中创建 Vec 并安全地释放它?
问题描述
我正在从 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()
}
如果我理解正确,.to_vec()
会将数据从切片复制到一个新的Vec
,因此仍然需要释放底层内存(因为切片的底层内存在被删除时不会被释放)。
处理上述问题的正确方法是什么?
- 我可以创建一个
Vec
拥有底层内存的所有权,当它被释放时Vec
被释放? - 如果不是,我应该在 Rust 的哪里/如何释放 C 函数分配的内存?
- 上面还有什么可以/应该改进的吗?
解决方案
我可以创建一个
Vec
拥有底层内存的所有权,当它被释放时Vec
被释放?
不安全,不。除非指针最初来自(嗯,来自同一个内存分配器),否则您不得使用。否则,您将尝试释放分配器不知道的内存;一个非常糟糕的主意。Vec::from_raw_parts
Vec
请注意,同样的事情也适用于String::from_raw_parts
,因为 aString
是 a 的包装器Vec<u8>
。
我应该在 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() {}
您没有说明为什么需要将其设为 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() {}
也可以看看:
推荐阅读
- c# - C# MVC & React - 在 MS EDGE 上反应未完全加载(Cors 错误 SEC7120)
- git - 如何列出包含精心挑选的提交的 git 标签
- javascript - 从父组件修改子状态
- ios - 如何使用两条边设置 UIScreenEdgePanGestureRecognizer
- html - HTML SVG 作为背景,带有未缩放的右对齐内容
- gitlab - 根据条件运行 gitlab 管道
- python - 使用索引数组替换列表中的指定元素(python) - 错误消息
- node.js - 快速计算MongoDB中大量文档的统计量
- elasticsearch - Elassandra中的日期范围搜索
- rest - 使用 PHP cUrl 将文件上传到 JIRA REST API