I am creating bindings in Rust for a C library and Bindgen generated enums like:
// 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,
}
I need to convert these to:
// Rust
pub enum QoSHistoryPolicy {
SystemDefault = 0,
KeepLast = 1,
KeepAll = 2,
Unknown = 3,
}
When importing constant values from this C library:
// C library
const rmw_qos_history_policy_t some_value_from_C = RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;
I would like to do something like:
let some_value: QoSHistoryPolicy = some_value_from_C;
How can I go about it?
The compiler does not inspect enums for ABI compatibility, and as such does not provide a direct way to convert values between these types. A few possible solutions follow.
This is trivial and safe, albeit leading to exhaustive code.
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,
}
}
}
Rust allows you to convert field-less enums into an integer type using the as
operator. The opposite conversion is not always safe however. Derive FromPrimitive
using the num
crate to obtain the missing piece.
#[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")
}
}
In the event that you just want an abstraction to low-level bindings, you might go without a new enum type.
#[repr(transparent)]
pub struct QoSHistoryPolicy(rmw_qos_history_policy_t);
The type above contains the same information and binary representation, but can expose an encapsulated API. The conversion from the low-level type to the high-level type becomes trivial. The main downside is that you lose pattern matching over its variants.
When absolutely sure that the two enums are equivalent in their binary representation, you can transmute
between them. The compiler won't help you here, this is far from recommended.
unsafe {
let policy: QoSHistoryPolicy = std::mem::transmute(val);
}
See also: