首页 > 解决方案 > 由于内存布局,将标记的 C 联合转换为 Rust 枚举的问题

问题描述

我正在尝试与结构如下的 C 事件 API 进行交互:

struct EventA {
    int type;
    int data1;
    int data2;
};

struct EventB {
    int type;
    int data1;
};

union Event {
    int type;
    EventA eventA;
    EventB eventB;
};

//grabs the next event from the queue.
void pollEvent(Event *event); 

在 C API 中,该type字段用于确定发生了什么类型的事件。这本质上是一个标记的联合,或者 Rust 中的枚举。我知道 Rust 允许你选择枚举的底层标签,#[repr(type)]但是我相信 Rust 不能保证标签是枚举的第一个字段。(或者它总是最后一个字段?)这使得将 API 转换为 Rust 时有点恶心,因为我需要某种类型的中间人结构来轮询然后匹配其类型,将其转换为枚举,然后用户将匹配枚举。

如果我知道标签是枚举的第一个字段,我知道枚举和联合具有相同的内存表示,我可以将它作为指针传递。

我想做的事:

use::std::os::raw::c_int;

#[repr(C)]
#[derive(Copy, Clone)]
pub struct EventA {
    pub data1: c_int,
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct EventB {
    pub data1: c_int,
    pub data2: c_int,
}

#[repr(i32)] //I'm assuming c_int is 32 bit.
#[derive(Copy, Clone)]
pub enum Event {
    // <---- Put the tag is here, followed the union memory, 
    A(EventA),
    B(EventB),
}

extern {
    pub fn pollEvents(*mut Event);
}

pub fn poll_events(*mut Event) {
    unsafe {
        pollEvents(*mut Event);
    }
}

标签: crust

解决方案


当您在基本上允许您想要的枚举上使用时,有一个RFC可以保证某些布局。#[repr(C, Int)]请注意,与往常一样,Rust 只做最好的猜测,您应该在编译时添加内存布局测试。


推荐阅读