如何将 bindgen 生成的 C 样式枚举转换为另一个枚举?

deb*_*0ch 2 enums rust rust-bindgen

我正在为 C 库和 Bindgen 生成的枚举在 Rust 中创建绑定,例如:

// Rust
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum rmw_qos_history_policy_t {
    RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT = 0,
    RMW_QOS_POLICY_HISTORY_KEEP_LAST = 1,
    RMW_QOS_POLICY_HISTORY_KEEP_ALL = 2,
    RMW_QOS_POLICY_HISTORY_UNKNOWN = 3,
}
Run Code Online (Sandbox Code Playgroud)

我需要将这些转换为:

// Rust
pub enum QoSHistoryPolicy {
    SystemDefault = 0,
    KeepLast = 1,
    KeepAll = 2,
    Unknown = 3,
}
Run Code Online (Sandbox Code Playgroud)

从这个 C 库导入常量值时:

// Rust
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum rmw_qos_history_policy_t {
    RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT = 0,
    RMW_QOS_POLICY_HISTORY_KEEP_LAST = 1,
    RMW_QOS_POLICY_HISTORY_KEEP_ALL = 2,
    RMW_QOS_POLICY_HISTORY_UNKNOWN = 3,
}
Run Code Online (Sandbox Code Playgroud)

我想做类似的事情:

let some_value: QoSHistoryPolicy = some_value_from_C;
Run Code Online (Sandbox Code Playgroud)

我该怎么办?

E_n*_*ate 7

编译器不检查枚举的 ABI 兼容性,因此不提供在这些类型之间转换值的直接方法。以下是一些可能的解决方案。

1.一对一匹配

这是微不足道的和安全的,尽管会导致详尽的代码。

impl From<rmw_qos_history_policy_t> for QoSHistoryPolicy {
    fn from(x: rmw_qos_history_policy_t) -> Self {
        use rmw_qos_history_policy_t::*;
        match x {
            RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT => QoSHistoryPolicy::SystemDefault,
            RMW_QOS_POLICY_HISTORY_KEEP_LAST => QoSHistoryPolicy::KeepLast,
            RMW_QOS_POLICY_HISTORY_KEEP_ALL => QoSHistoryPolicy::KeepAll,
            RMW_QOS_POLICY_HISTORY_UNKNOWN => QoSHistoryPolicy::Unknown,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

2. Casting + FromPrimitive

Rust 允许您使用as运算符将无字段枚举转换为整数类型。然而,相反的转换并不总是安全的。FromPrimitive使用num板条箱导出以获取丢失的部分。

#[derive(FromPrimitive)]
pub enum QoSHistoryPolicy { ... }

impl From<rmw_qos_history_policy_t> for QoSHistoryPolicy {
    fn from(x: rmw_qos_history_policy_t) -> Self {
        FromPrimitive::from_u32(x as _).expect("1:1 enum variant matching, all good")
    }
}
Run Code Online (Sandbox Code Playgroud)

3. 需要枚举吗?

如果您只想抽象到低级绑定,您可能不需要新的枚举类型。

#[repr(transparent)]
pub struct QoSHistoryPolicy(rmw_qos_history_policy_t);
Run Code Online (Sandbox Code Playgroud)

上面的类型包含相同的信息和二进制表示,但可以公开封装的 API。从低级类型到高级类型的转换变得微不足道。主要的缺点是你失去了与其变体的模式匹配。

4. 靠你自己

绝对确定两个枚举在二进制表示中是等效的时,您可以transmute在它们之间。编译器不会在这里帮助您,这远非推荐。

unsafe {
    let policy: QoSHistoryPolicy = std::mem::transmute(val);
}
Run Code Online (Sandbox Code Playgroud)

也可以看看: