Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

memory

rust

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 DbaxCells are cleaned up correctly and no warning is given?

like image 955
Delta_Fore Avatar asked Jun 09 '15 20:06

Delta_Fore


1 Answers

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 representation.

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
}
like image 98
Shepmaster Avatar answered Nov 02 '22 06:11

Shepmaster