简体   繁体   中英

Correct idiom for freeing repr(C) structs using Drop trait

This code works fine but gives a compiler warning on 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);
        }
    }
}

It links to a C library and creates/deletes cell objects correctly. However it gives a warning

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     }

What is the right way to do this to ensure that these DbaxCell s are cleaned up correctly and no warning is given?

I think you are conflating two concepts. A struct should be repr(C) if you wish for the layout of the struct to directly correspond to the layout of the struct as a C compiler would lay it out. That is, it has the same in-memory repr esentation.

However, you don't need that if you are just holding a raw pointer, and are not going to pass the holding structure back to C. The short solution in this case is "remove repr(C) ".

To explain a bit more about the error...

implementing Drop adds hidden state to types, possibly conflicting with #[repr(C)]

This was discussed in issue 24585 . When an object is dropped, a hidden flag (the "state") is set that indicates that the object has been dropped, preventing multiple drops from occurring. However, hidden bits mean that what you see in Rust does not correspond to what the bytes of the struct would look like in C, negating the purpose of the repr(C) .

As cribbed from @bluss :

Low level programmers, don't worry: future Rust will remove this drop flag entirely.

And

Use repr(C) to pass structs in FFI, and use Drop on "regular Rust" structs if you need to. If you need both, embed the repr(C) struct inside the regular struct.

Imagine we had a library that exposes a C struct with two 8-bit numbers, and methods that take and return that struct:

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

tuple_t tuple_increment(tuple_t position);

In this case, you would definitely want to mimic that struct and match the C representation in Rust:

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

However, if the library returned pointers to the struct, and you never need to poke into it (the structure is opaque ) then you don't need to worry about repr(C) :

void tuple_increment(tuple_t *position);

Then you can just use that pointer and implement Drop:

struct TuplePointer(*mut libc::c_void);

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM