Ho creato un wrapper attorno a una libreria C che crea un dispositivo che è necessario chiudere esplicitamente.Come eseguire il wrapping di una libreria nativa con semantica init/exit
Scrivere le funzioni FFI non elaborate era semplice, ma come posso renderlo ergonomico in un wrapper di livello superiore?
In particolare, dovrei farlo nello stile RAII e utilizzare solo Drop
per garantire che la chiusura venga chiamata quando esce dall'ambito, invece di esporre il metodo close()
al chiamante? Qual è il modo più idiomatico in Rust?
ci sono fondamentalmente 3 opzioni:
- involucro sottile che richiede gli stessi
close()
chiamate come la libreria C; - stile RAII che non ha esposto
close()
, solo un'implementazioneDrop
; - C#
dispose()
implementazione in stile che tiene traccia dello stato chiuso e consente entrambe le forme di chiusura.
L'ultima forma assomiglia a questo:
pub enum NativeDevice {} // Opaque pointer to C struct
fn ffi_open_native_device() -> *mut NativeDevice { unimplemented!() }
fn ffi_close_native_device(_: *mut NativeDevice) {}
fn ffi_foo(_: *mut NativeDevice, _: u32) -> u32 { unimplemented!() }
pub struct Device {
native_device: *mut NativeDevice,
closed: bool,
}
impl Device {
pub fn new() -> Device {
Device {
native_device: ffi_open_native_device(),
closed: false,
}
}
pub fn foo(&self, arg: u32) -> u32 {
ffi_foo(self.native_device, arg)
}
pub fn close(&mut self) {
if !self.closed {
ffi_close_native_device(self.native_device);
self.closed = true;
}
}
}
impl Drop for Device {
fn drop(&mut self) {
self.close();
}
}
Opzione numero 2. I mai visto l'opzione 1 o 3 usata in ruggine. Nel tuo esempio, cosa succede quando 'close' viene chiamato più di una volta? – malbarbo
Ben avvistato, l'if-closed dovrebbe ovviamente andare nel metodo close() e non nel drop(). Modificato. –