我围绕一个C库创建了一个包装器,它创建了一个必须明确关闭的设备.
编写原始FFI函数很简单,但如何在更高级别的包装器中使其符合人体工程学?
具体来说,我应该使用RAII样式并且仅Drop用于确保在超出范围时调用close,而不是将close()方法暴露给调用者?哪种方式是Rust最惯用的?
基本上有3种选择:
close()C库相同的调用;close(),只有一个Drop实现;dispose()样式实现,跟踪关闭状态并允许两种形式的关闭.最后一个表单如下所示:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
按照惯例,我相信您只会实施Drop. 我不知道有任何标准库类型实现了允许用户手动(调用方法)和自动(通过删除)处置资源的模式。
这甚至导致了一些奇怪的情况。例如,通过类似的函数关闭文件fclose可能会产生错误。但是,Rust 析构函数无法向用户返回失败代码。这意味着类似的错误会被吞掉。
这导致您可能想要同时支持两者。您的close方法可以返回 a Result,然后您可以忽略 中的结果Drop。
正如Jsor 指出的那样,您可能希望您的close方法按值接受类型。我还意识到您可以使用一个NULL值来指示该值是否已关闭。
use std::ptr;
enum NativeDevice {} // Opaque pointer to C struct
fn ffi_open_native_device() -> *mut NativeDevice {
0x1 as *mut NativeDevice
}
fn ffi_close_native_device(_: *mut NativeDevice) -> u8 {
println!("Close was called");
0
}
struct Device {
native_device: *mut NativeDevice,
}
impl Device {
fn new() -> Device {
let dev = ffi_open_native_device();
assert!(!dev.is_null());
Device {
native_device: dev,
}
}
fn close(mut self) -> Result<(), &'static str> {
if self.native_device.is_null() { return Ok(()) }
let result = ffi_close_native_device(self.native_device);
self.native_device = ptr::null_mut();
// Important to indicate that the device has already been cleaned up
match result {
0 => Ok(()),
_ => Err("Something wen't boom"),
}
}
}
impl Drop for Device {
fn drop(&mut self) {
if self.native_device.is_null() { return }
let _ = ffi_close_native_device(self.native_device);
// Ignoring failure to close here!
}
}
fn main() {
let _implicit = Device::new();
let explicit = Device::new();
explicit.close().expect("Couldn't close it");
}
Run Code Online (Sandbox Code Playgroud)
如果关闭设备时可能发生某种可恢复的错误,您可以将对象返回给用户以重试:
enum Error {
RecoverableError(Device),
UnknownError,
}
fn close(mut self) -> Result<(), Error> {
if self.native_device.is_null() {
return Ok(());
}
let result = ffi_close_native_device(self.native_device);
match result {
0 => {
self.native_device = ptr::null_mut();
// Important to indicate that the device has already been cleaned up
Ok(())
},
1 => Err(Error::RecoverableError(self)),
_ => {
self.native_device = ptr::null_mut();
// Important to indicate that the device has already been cleaned up
Err(Error::UnknownError)
},
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
134 次 |
| 最近记录: |