如何在没有泄漏内存的情况下从C实现Rust结构和方法作为Vec?

时间:2016-09-05 07:29:58

标签: c struct binding rust ffi

我正在为使用嵌入式构造函数和析构函数的C库编写Rust bindings。 C头的Raw 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);

这是git2-rs中的安全Rust代码的实现:

/// 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) };
    }
}

同样适用于ListReferenceReference

我需要使用ListEntityVec<Entity>一样(迭代器,切片用于排序等),但我无法实现它。在我的实施版本中,我无法创建切片:from_raw_parts会返回UdbEntity上的切片,而非Entity

当我Vec<Entity> EntityList以及稍后编辑Vec<Entity>(移动它)时,EntityList将被删除并释放已分配的列表*mut UdbEntity。我也需要正确的生活时间。

我反转了一些简单的结构(例如KindListKind)来编写纯Rust代码,但我认为存在更惯用的路径。

1 个答案:

答案 0 :(得分:1)

问题是你的代码中有两个相当不相交的结构。一方面,你有ListEntity拥有原始数组UdbEntity并在需要时释放它,另一方面你有Entity包裹UdbEntity ,但ListEntity没有以任何方式引用。

这里有两个选择。

  1. UdbEntity数组转换为Entity数组,在这种情况下,您将能够创建它的切片。为此,他们需要具有相同的内存表示。
  2. Entity分开创建UdbEntity的向量,然后返回它们。
  3. 假设第一种方法是安全的,我会坚持下去。如果没有,那么第二个可以工作。在这两种情况下,Entity的数组都应归ListEntity所有,以便正确管理内存。我可能会抛弃PhantomData中的Entity并简单地返回对它们的引用。