Правильная идиома для освобождения структур репрезентации (C) с использованием черты Drop

Этот код работает нормально, но дает предупреждение компилятора в Rust nightly (1.2)

#[repr(C)]
struct DbaxCell { 
    cell: *const c_void
}

#[link(name="CDbax", kind="dylib")] 
extern {
    fn new_dCell(d: c_double) -> *const c_void;
    fn deleteCell(c: *const c_void);
}

impl DbaxCell {
    fn new(x: f64) -> DbaxCell {
        unsafe {
            DbaxCell { cell: new_dCell(x) }
        }
    }
}

impl Drop for DbaxCell {
    fn drop(&mut self) {
        unsafe {
            deleteCell(self.cell);
        }
    }
}

Он ссылается на библиотеку C и создает/удаляет объекты ячейки правильно. Однако он дает предупреждение

src\lib.rs:27:1: 33:2 warning: implementing Drop adds hidden state to types, possibly conflicting with `#[repr(C)]`, #[warn(drop_with_repr_extern)] on by default
\src\lib.rs:27 impl Drop for DbaxCell {
\src\lib.rs:28     fn drop(&mut self) {
\src\lib.rs:29         unsafe {
\src\lib.rs:30             deleteCell(self.cell);
\src\lib.rs:31         }
\src\lib.rs:32     }

Каков правильный способ сделать это, чтобы убедиться, что эти DbaxCell очищены правильно и не выдано предупреждение?

Ответ 1

Я думаю, что вы объединяете два понятия. Строка должна быть repr(C), если вы хотите, чтобы макет структуры напрямую соответствовал макету структуры, поскольку компилятор C выложил бы ее. То есть, он имеет ту же самую информацию repr в памяти.

Однако вам не нужно, если вы просто держите необработанный указатель и не собираетесь передавать холдинговую структуру обратно на C. Коротким решением в этом случае является "remove repr(C)".

Объяснить немного больше об ошибке...

реализация Drop добавляет скрытое состояние в типы, возможно, конфликтует с #[repr(C)]

Это обсуждалось в вопрос 24585. Когда объект отбрасывается, устанавливается скрытый флаг ( "состояние" ), который указывает, что объект был удален, предотвращая появление нескольких капель. Однако скрытые биты означают, что то, что вы видите в Rust, не соответствует тому, как бы выглядели байты структуры в C, отрицая цель repr(C).

Как cribbed from @bluss:

Программисты низкого уровня, не волнуйтесь: будущий Rust полностью удалит этот флаг кавычки.

и

Используйте repr(C) для передачи структур в FFI и используйте Drop в структурах "регулярного Rust", если вам нужно. Если вам нужны оба, вставьте структуру repr(C) внутри обычной структуры.

Представьте, что у нас была библиотека, которая предоставляет C-структуру с двумя 8-битными номерами и методы, которые принимают и возвращают эту структуру:

typedef struct {
    char a;
    char b;
} tuple_t;

tuple_t tuple_increment(tuple_t position);

В этом случае вы определенно захотите имитировать эту структуру и сопоставить представление C в Rust:

#[repr(C)]
struct Tuple {
    a: libc::char,
    b: libc::char,
}

Однако, если библиотека вернула указатели на структуру, и вам никогда не нужно в нее вставлять (структура непрозрачна), вам не нужно беспокоиться о repr(C):

void tuple_increment(tuple_t *position);

Затем вы можете просто использовать этот указатель и реализовать Drop:

struct TuplePointer(*mut libc::c_void);

impl Drop for TuplePointer {
    // Call the appropriate free function from the library
}