我尝试创建到[医]二线菌的FFI绑定,用C编写,这里我偶然发现了这个函数。
modbus_set_error_recovery(ctx,
MODBUS_ERROR_RECOVERY_LINK |
MODBUS_ERROR_RECOVERY_PROTOCOL);第二个参数定义为
typedef enum
{
MODBUS_ERROR_RECOVERY_NONE = 0,
MODBUS_ERROR_RECOVERY_LINK = (1<<1),
MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2)
} modbus_error_recovery_mode;我的bindgen-generated绑定如下:
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum modbus_error_recovery_mode {
MODBUS_ERROR_RECOVERY_NONE = 0,
MODBUS_ERROR_RECOVERY_LINK = 2,
MODBUS_ERROR_RECOVERY_PROTOCOL = 4,
}和
extern "C" {
pub fn modbus_set_error_recovery(ctx: *mut modbus_t,
error_recovery:
modbus_error_recovery_mode)
-> ::std::os::raw::c_int;
}到目前为止,我的安全界面是这样的:
pub fn set_error_recovery(&mut self, error_recovery_mode: ErrorRecoveryMode) -> Result<()> {
unsafe {
match ffi::modbus_set_error_recovery(self.ctx, error_recovery_mode.to_c()) {
-1 => bail!(Error::last_os_error()),
0 => Ok(()),
_ => panic!("libmodbus API incompatible response"),
}
}
}和
use std::ops::BitOr;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ErrorRecoveryMode {
NONE = 0,
Link = 2,
Protocol = 4,
}
impl ErrorRecoveryMode {
pub fn to_c(self) -> ffi::modbus_error_recovery_mode {
match self {
NONE => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_NONE,
Link => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_LINK,
Protocol => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_PROTOCOL,
}
}
}
impl BitOr for ErrorRecoveryMode {
type Output = Self;
fn bitor(self, rhs: ErrorRecoveryMode) -> ErrorRecoveryMode {
self | rhs
}
}如果我这样调用set_error_recovery,就会触发堆栈溢出。
assert!(modbus.set_error_recovery(ErrorRecoveryMode::Link | ErrorRecoveryMode::Protocol).is_ok())错误是
thread 'set_error_recovery' has overflowed its stack
fatal runtime error: stack overflow发布于 2017-10-20 07:59:08
问题是C的enum和Rust的enum是非常不同的。特别是,C允许enum具有绝对的任何值,无论该值是否与变体相对应。
铁锈不会。锈蚀依赖于enum,它只有定义变量的单个值,否则就会面临未定义行为的风险。
您拥有的不是枚举(在锈蚀意义上),而是位标志,您需要bitflags机箱。
至于堆栈溢出,这仅仅是因为您根据自身定义了BitOr实现;该代码是无条件递归的。
发布于 2017-10-20 13:07:16
作为DK。提到:
enum和Rust's enum有不同的限制。幸运的是,Bindgen理解位标志。如果在传递bitfield-enum标志时或通过使用Builder::bitfield_enum生成标头
bindgen --bitfield-enum modbus_error_recovery_mode fake-modbus.hBindgen将为每个C枚举值、一个新类型的包装器和Bit*特性的实现生成常量:
// Many implementation details removed
pub struct modbus_error_recovery_mode(pub ::std::os::raw::c_uint);
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_NONE: modbus_error_recovery_mode =
modbus_error_recovery_mode(0);
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_LINK: modbus_error_recovery_mode =
modbus_error_recovery_mode(2);
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_PROTOCOL: modbus_error_recovery_mode =
modbus_error_recovery_mode(4);
impl ::std::ops::BitOr<modbus_error_recovery_mode> for modbus_error_recovery_mode {}
impl ::std::ops::BitOrAssign for modbus_error_recovery_mode {}
impl ::std::ops::BitAnd<modbus_error_recovery_mode> for modbus_error_recovery_mode {}
impl ::std::ops::BitAndAssign for modbus_error_recovery_mode {}
extern "C" {
pub fn modbus_set_error_recovery(
ctx: *mut modbus_t,
error_recovery: modbus_error_recovery_mode,
) -> ::std::os::raw::c_int;
}如何向公众公开由bindgen生成的常量
当然,为非锈蚀代码创建一个惯用的Rust是很难的。我可能会尝试这样的方法:
#[derive(Debug)]
struct Modbus(*mut raw::modbus_t);
#[derive(Debug)]
struct Error;
#[derive(Debug, Copy, Clone)]
enum ErrorRecovery {
Link,
Protocol,
}
impl ErrorRecovery {
fn as_raw(&self) -> raw::modbus_error_recovery_mode {
use ErrorRecovery::*;
match *self {
Link => raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_LINK,
Protocol => raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_PROTOCOL,
}
}
}
impl Modbus {
fn set_error_recovery(&mut self, flags: Option<&[ErrorRecovery]>) -> Result<(), Error> {
let flag = flags.unwrap_or(&[]).iter().fold(
raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_NONE,
|acc, v| acc | v.as_raw(),
);
let res = unsafe { raw::modbus_set_error_recovery(self.0, flag) };
Ok(()) // real error checking
}
}https://stackoverflow.com/questions/46844611
复制相似问题