如何强制联合表现得好像只有一种类型?

问题描述 投票:2回答:1

我正在尝试为epoll Linux API编写一个包装器。我分叉了this存储库,但是这个crate不使用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,有很多不安全的代码,我想从用户端删除尽可能多的不安全代码。目前它看起来很丑陋。

rust
1个回答
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来编码我们在联合中隐藏的指针的具体类型,从而避免在我们去检索它时不匹配它。

严格来说,这个特性是不需要的,但我认为它提供了一些不错的内置文档。

也可以看看:

© www.soinside.com 2019 - 2024. All rights reserved.