使用Drop trait释放repr(C)结构的正确习惯用法

时间:2015-06-09 20:16:37

标签: c memory rust

此代码工作正常,但在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 个答案:

答案 0 :(得分:9)

我认为你正在混淆两个概念。如果您希望结构的布局直接对应结构的布局,那么结构应该是repr(C),因为C编译器会将其布局。也就是说,它具有相同的内存repr表示。

但是,如果您只是持有一个原始指针,并且不打算将保持结构传递回C,则需要。在这种情况下,简短的解决方案是“remove { {1}}”。

更多地解释错误...

  

实现Drop会向类型添加隐藏状态,可能与repr(C)

冲突

这在issue 24585中讨论过。删除对象时,会设置隐藏标志(“状态”),表示该对象已被删除,从而防止发生多次丢弃。但是,隐藏位意味着您在Rust中看到的内容与C中结构的字节不一致,从而否定了#[repr(C)]的目的。

作为cribbed from @bluss

  

低级程序员,不要担心:future Rust将完全删除此丢弃标记。

  

如果需要,使用repr(C)在FFI中传递结构,并在“常规Rust”结构上使用repr(C)。如果您需要两者,请将Drop结构嵌入常规结构中。

想象一下,我们有一个库,它公开了一个带有两个8位数字的C结构,以及获取并返回该结构的方法:

repr(C)

在这种情况下,您肯定希望模仿该结构并匹配Rust中的C表示:

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

tuple_t tuple_increment(tuple_t position);

但是,如果库返回指向结构的指针,并且您永远不需要戳它(结构是不透明),那么您不必担心#[repr(C)] struct Tuple { a: libc::char, b: libc::char, }

repr(C)

然后你可以使用那个指针并实现Drop:

void tuple_increment(tuple_t *position);