我正在尝试为epoll Linux API编写一个包装器。我分叉this存储库,但此包不使用union
API使用的epoll
类型。我决定使用Rust的C union功能来创建一个完整的包装器,用户不需要使用不安全的代码。
这个联盟给我带来了一些麻烦。
如何在编译时将联合使用的类型锁定为一种类型? epoll的联盟无法区分;你只能通过epoll fd使用union的一个成员。好吧,你可以,但这不安全。
用户可以使用枚举类型作为联合的ptr
字段来使用多种类型,但这样做是安全的,因为它将使用Rust的enum
。
我用“通用”或“宏”搜索但我找不到任何符合我意愿的方法。
extern crate libc;
#[derive(Clone, Copy)]
pub union Data {
pub ptr: *mut libc::c_void,
pub fd: std::os::unix::io::RawFd,
pub u32: libc::uint32_t,
pub u64: libc::uint64_t,
}
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy)]
pub struct Event {
pub data: Data,
}
impl Event {
fn new(data: Data) -> Event {
Event { data: data }
}
}
fn main() {
let event = Event::new(Data {
ptr: Box::into_raw(Box::new(42)) as *mut libc::c_void,
});
unsafe { Box::from_raw(event.data.ptr) };
}
我想要那样的东西:
fn main() {
let event = event!(ptr, Box::new(42));
let _ = event.ptr();
let _ = event.fd(); // fails to compile; we can only use ptr
}
我的叉子可以找到here。我不知道宏,通用或其他什么是合适的解决方案。您可以查看我的代码,特别是the integration test,有很多不安全的代码,我想尽可能地从用户端删除不安全的代码。目前它看起来很丑陋。
答案 0 :(得分:3)
您可以将联合包装在另一种类型中。该类型将具有特定特征的通用参数。然后,您可以根据具体实现实现特定的方法集。
在这种情况下,我们将联合Data
包装在Event
类型中。 Event
具有实现EventMode
的任何类型的泛型类型参数。我们为具体类型Event<Fd>
和Event<Ptr>
:
extern crate libc;
use std::os::unix::io::RawFd;
use std::marker::PhantomData;
#[derive(Copy, Clone)]
pub union Data {
pub ptr: *mut libc::c_void,
pub fd: RawFd,
}
trait EventMode {}
#[derive(Debug, Copy, Clone)]
struct Fd {
_marker: PhantomData<RawFd>,
}
impl Fd {
fn new() -> Self {
Fd {
_marker: PhantomData,
}
}
}
impl EventMode for Fd {}
#[derive(Debug, Copy, Clone)]
struct Ptr<T> {
_marker: PhantomData<Box<T>>,
}
impl<T> Ptr<T> {
fn new() -> Self {
Ptr {
_marker: PhantomData,
}
}
}
impl<T> EventMode for Ptr<T> {}
#[derive(Copy, Clone)]
pub struct Event<M> {
pub data: Data,
mode: M,
}
impl Event<Fd> {
fn new_fd(fd: RawFd) -> Self {
Event {
data: Data { fd },
mode: Fd::new(),
}
}
fn fd(&self) -> RawFd {
unsafe { self.data.fd }
}
}
impl<T> Event<Ptr<T>> {
fn new_ptr(t: T) -> Self {
let ptr = Box::into_raw(Box::new(t)) as *mut _;
Event {
data: Data {
ptr: ptr,
},
mode: Ptr::new(),
}
}
fn ptr(&self) -> &T {
unsafe { &*(self.data.ptr as *const T) }
}
}
fn main() {
let event = Event::new_ptr(42);
println!("{}", event.ptr());
// event.fd();
}
尝试拨打event.fd()
会出错:
error[E0599]: no method named `fd` found for type `Event<Ptr<{integer}>>` in the current scope
--> src/main.rs:76:11
|
76 | event.fd();
| ^^
作为奖励,我们可以使用相同的PhantomData
来编码我们在联合中隐藏的指针的具体类型,从而避免在我们检索它时将其不匹配。
严格来说,这个特性不是需要,但我认为它提供了一些不错的内置文档。
另见: