联合体的模式匹配
联合体的模式匹配比较受限, 每次只能匹配其中的一个成员, 先看一个示例:
#![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()); }