如何在不泄漏内存的情况下将 Rust 结构和方法实现为 C 中的 Vec?
How to implement Rust struct and methods as Vec from C without leaking memory?
我正在为使用嵌入式构造函数和析构函数的 C 库编写 Rust bindings。 C 头文件的原始 Rust 代码:
// Opaque structures transform to enumerate
pub enum UdbEntity_ {}
pub enum UdbReference_ {}
...
pub type UdbEntity = *mut UdbEntity_;
pub type UdbReference = *mut UdbReference_;
...
// Return a non-allocated, permanent list of all entities. This list may be
// used in places where an allocated entity list is required and may be
// safely passed to udbListEntityFree().
pub fn udbListEntity(list: *mut *mut UdbEntity, items: *mut c_int);
// Free an allocated list of entities.
pub fn udbListEntityFree(list: *mut UdbEntity);
...
// Return an allocated list of all references for entity.
// Free the list with udbListReferenceFree().
pub fn udbListReference(entity : UdbEntity,
refs : *mut *mut UdbReference,
items : *mut c_int);
// Free the allocated references list.
pub fn udbListReferenceFree(refs: *mut UdbReference);
这是安全 Rust 代码的实现,如 git2-rs:
/// Structure of Entity.
pub struct Entity<'ents> {
raw: UdbEntity,
_marker: PhantomData<&'ents UdbEntity>,
}
/// Opaque structure of list of entities.
pub struct ListEntity<'db> {
raw: *mut UdbEntity,
len: usize,
_marker: PhantomData<&'db Db>,
}
/// An iterator over the Entity in list of entities.
pub struct EntityIter<'ents> {
range: Range<usize>,
ents: &'ents ListEntity<'ents>,
}
impl<'db> Drop for ListEntity<'db> {
fn drop(&mut self) {
unsafe { udbListEntityFree(self.raw) };
}
}
还有 ListReference
和 Reference
。
我需要像使用 Vec<Entity>
一样使用 ListEntity
(迭代器、用于排序的切片等),但我无法实现它。在我的实现版本中,我无法创建切片:from_raw_parts
returns 切片超过 UdbEntity
,而不是 Entity
.
当我将 Vec<Entity>
保留在 EntityList
中,稍后当我编辑 Vec<Entity>
(移动它)时,EntityList
被删除并释放分配的列表 *mut UdbEntity
.我也需要正确的生命周期。
我颠倒了一些简单的结构(例如 Kind
、ListKind
)来编写纯 Rust 代码,但我认为存在更惯用的路径。
问题是您的代码中有两个相当不相交的结构。一方面,您有一个 ListEntity
,它拥有 UdbEntity
的原始数组,并在需要时释放它,另一方面,您有一个 Entity
,它包装了 UdbEntity
, 但未以任何方式在 ListEntity
.
中引用
这里有两个选择。
- 将
UdbEntity
的数组转换为 Entity
的数组,在这种情况下,您将能够创建它的切片。为此,它们需要具有相同的内存表示。
- 从
UdbEntity
和 return 中分别创建一个 Entity
向量。
假设第一种方法是安全的,我会接受。如果没有,那么第二个可以工作。在这两种情况下,Entity
的数组应该由 ListEntity
拥有,以便正确管理内存。我可能会放弃 Entity
中的 PhantomData
并简单地 return 引用它们。
我正在为使用嵌入式构造函数和析构函数的 C 库编写 Rust bindings。 C 头文件的原始 Rust 代码:
// Opaque structures transform to enumerate
pub enum UdbEntity_ {}
pub enum UdbReference_ {}
...
pub type UdbEntity = *mut UdbEntity_;
pub type UdbReference = *mut UdbReference_;
...
// Return a non-allocated, permanent list of all entities. This list may be
// used in places where an allocated entity list is required and may be
// safely passed to udbListEntityFree().
pub fn udbListEntity(list: *mut *mut UdbEntity, items: *mut c_int);
// Free an allocated list of entities.
pub fn udbListEntityFree(list: *mut UdbEntity);
...
// Return an allocated list of all references for entity.
// Free the list with udbListReferenceFree().
pub fn udbListReference(entity : UdbEntity,
refs : *mut *mut UdbReference,
items : *mut c_int);
// Free the allocated references list.
pub fn udbListReferenceFree(refs: *mut UdbReference);
这是安全 Rust 代码的实现,如 git2-rs:
/// Structure of Entity.
pub struct Entity<'ents> {
raw: UdbEntity,
_marker: PhantomData<&'ents UdbEntity>,
}
/// Opaque structure of list of entities.
pub struct ListEntity<'db> {
raw: *mut UdbEntity,
len: usize,
_marker: PhantomData<&'db Db>,
}
/// An iterator over the Entity in list of entities.
pub struct EntityIter<'ents> {
range: Range<usize>,
ents: &'ents ListEntity<'ents>,
}
impl<'db> Drop for ListEntity<'db> {
fn drop(&mut self) {
unsafe { udbListEntityFree(self.raw) };
}
}
还有 ListReference
和 Reference
。
我需要像使用 Vec<Entity>
一样使用 ListEntity
(迭代器、用于排序的切片等),但我无法实现它。在我的实现版本中,我无法创建切片:from_raw_parts
returns 切片超过 UdbEntity
,而不是 Entity
.
当我将 Vec<Entity>
保留在 EntityList
中,稍后当我编辑 Vec<Entity>
(移动它)时,EntityList
被删除并释放分配的列表 *mut UdbEntity
.我也需要正确的生命周期。
我颠倒了一些简单的结构(例如 Kind
、ListKind
)来编写纯 Rust 代码,但我认为存在更惯用的路径。
问题是您的代码中有两个相当不相交的结构。一方面,您有一个 ListEntity
,它拥有 UdbEntity
的原始数组,并在需要时释放它,另一方面,您有一个 Entity
,它包装了 UdbEntity
, 但未以任何方式在 ListEntity
.
这里有两个选择。
- 将
UdbEntity
的数组转换为Entity
的数组,在这种情况下,您将能够创建它的切片。为此,它们需要具有相同的内存表示。 - 从
UdbEntity
和 return 中分别创建一个Entity
向量。
假设第一种方法是安全的,我会接受。如果没有,那么第二个可以工作。在这两种情况下,Entity
的数组应该由 ListEntity
拥有,以便正确管理内存。我可能会放弃 Entity
中的 PhantomData
并简单地 return 引用它们。