联合体的模式匹配
联合体的模式匹配比较受限, 每次只能匹配其中的一个成员, 先看一个示例:
#![allow(non_camel_case_types)]
#![allow(clippy::module_name_repetitions)]
#![allow(dead_code)]
use std::ffi::c_void;
#[repr(C)]
#[derive(Clone, Copy)]
pub union epoll_data_t {
pub ptr: *const c_void,
pub fd: i32,
pub v_u32: u32,
pub v_u64: u64,
}
fn main() {
let data = epoll_data_t { fd: 2 };
unsafe {
match data {
epoll_data_t { fd: 2 } => println!("stdout fd"),
epoll_data_t { v_u32: 42 } => println!("u32 is 42"),
epoll_data_t { v_u64: 28 } => println!("u64 is 28"),
epoll_data_t { ptr } => if ptr.is_null() {
eprintln!("NULL ptr")
} else {
println!("ptr: {ptr:?}")
},
}
}
}
有时, 需要配合支持 C 语言中常用的 tagged-union 写法, 比如下面的例子是对 X11 的 XEvent 的封装:
use std::ffi::c_void;
use std::ptr;
#[repr(u8)]
#[derive(Debug, Default, Clone, Copy)]
pub enum EventType {
#[default]
Any,
Keyboard,
Button,
Map,
}
pub type WindowId = i32;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct XAnyEvent {
pub event_type: EventType,
pub serial: usize,
pub send_event: bool,
pub display: *const c_void,
pub window: WindowId,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct XKeyEvent {
pub event_type: EventType,
pub serial: usize,
pub send_event: bool,
pub display: *const c_void,
pub window: WindowId,
pub root: WindowId,
pub x: i32,
pub y: i32,
pub x_root: i32,
pub y_root: i32,
pub keycode: u32,
}
impl XKeyEvent {
#[must_use]
#[inline]
pub fn new(window: WindowId, x: i32, y: i32, keycode: u32) -> Self {
Self {
event_type: EventType::Keyboard,
serial: 0,
send_event: false,
display: ptr::null(),
window,
root: window,
x,
y,
x_root: x,
y_root: y,
keycode,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct XButtonEvent {
pub event_type: EventType,
pub serial: usize,
pub send_event: bool,
pub display: *const c_void,
pub window: WindowId,
pub root: WindowId,
pub sub_window: WindowId,
pub x: i32,
pub y: i32,
pub state: u32,
pub button: u32,
}
#[repr(C)]
pub union XEvent {
pub event_type: EventType,
pub xany: XAnyEvent,
pub xkey: XKeyEvent,
pub xbutton: XButtonEvent,
}
fn main() {
assert_eq!(size_of::<XEvent>(), 64);
let event = XEvent {
xkey: XKeyEvent::new(0x128a8, 40, 80, 95)
};
unsafe {
match event.event_type {
EventType::Any => println!("generic event"),
EventType::Keyboard => {
let x = event.xkey.x;
let y = event.xkey.y;
let keycode = event.xkey.keycode;
println!("keyboard event at ({x}, {y}), keycode: 0x{keycode:0X}");
}
_ => eprintln!("Unhandled events"),
}
}
}
或者, 使用另一种风格的 tagged-union 写法, 这种的要更清晰一些. 下面这个例子支持单精度和双精度的方式来存储一个坐标点:
#![allow(dead_code)]
#[repr(u32)]
pub enum PointPrecision {
F32,
F64,
}
#[repr(C)]
union PointValue {
v_f32: f32,
v_f64: f64,
}
#[repr(C)]
struct Point {
tag: PointPrecision,
v: PointValue,
}
impl Point {
pub const fn new_f32(value: f32) -> Self {
Self {
tag: PointPrecision::F32,
v: PointValue { v_f32: value },
}
}
pub const fn new_f64(value: f64) -> Self {
Self {
tag: PointPrecision::F64,
v: PointValue { v_f64: value },
}
}
#[allow(clippy::match_like_matches_macro)]
pub fn is_zero(&self) -> bool {
unsafe {
match self {
Self { tag: PointPrecision::F32, v: PointValue { v_f32: 0.0 } } => true,
Self { tag: PointPrecision::F64, v: PointValue { v_f64: 0.0 } } => true,
_ => false,
}
}
}
}
fn main() {
assert_eq!(size_of::<Point>(), 16);
let point = Point::new_f32(3.12);
assert!(!point.is_zero());
}