Как преобразовать перечисление в стиле C, созданное bindgen, в другое перечисление?
Я создаю привязки в Rust для библиотеки C и перечислений, сгенерированных Bindgen, например:
// 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,
}
Мне нужно преобразовать их в:
// Rust
pub enum QoSHistoryPolicy {
SystemDefault = 0,
KeepLast = 1,
KeepAll = 2,
Unknown = 3,
}
При импорте постоянных значений из этой библиотеки C:
// C library
const rmw_qos_history_policy_t some_value_from_C = RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;
Я бы хотел сделать что-то вроде:
let some_value: QoSHistoryPolicy = some_value_from_C;
Как я могу это сделать?
2 ответа
Компилятор не проверяет перечисления на совместимость с 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,
}
}
}
2. Кастинг + 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")
}
}
3. Нужен enum?
В случае, если вам просто нужна абстракция для низкоуровневых привязок, вы можете обойтись без нового типа перечисления.
#[repr(transparent)]
pub struct QoSHistoryPolicy(rmw_qos_history_policy_t);
Приведенный выше тип содержит ту же информацию и двоичное представление, но может предоставлять инкапсулированный API. Преобразование из низкоуровневого типа в высокоуровневый становится тривиальным. Основным недостатком является то, что вы теряете сопоставление с образцом по его вариантам.
4. Вы сами по себе
Когда вы абсолютно уверены, что два перечисления эквивалентны в своем двоичном представлении, вы можетеtransmute
между ними. Компилятор здесь не поможет, это далеко не рекомендуется.
unsafe {
let policy: QoSHistoryPolicy = std::mem::transmute(val);
}
Смотрите также:
Похоже, это хороший кандидат на From
черта наQoSHistoryPolicy
.
impl From<rmw_qos_history_policy_t> for QoSHistoryPolicy {
fn from(raw: rmw_qos_history_policy_t) -> Self {
match raw {
rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT => QoSHistoryPolicy::SystemDefault,
rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_KEEP_LAST => QoSHistoryPolicy::KeepLast,
rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_KEEP_ALL => QoSHistoryPolicy::KeepAll,
rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_UNKNOWN => QoSHistoryPolicy::Unknown
}
}
}
так что теперь это должно работать
let some_value: QoSHistoryPolicy = some_value_from_C.into();