如何为winit的KeyCode枚举变体存储布尔值?替代HashMap方案
用数组替代HashMap存储winit按键状态的方案
核心思路
winit的KeyCode枚举提供了静态数组VARIANTS,包含所有按键变体。我们可以基于这个数组的长度创建固定大小的布尔数组,再通过VARIANTS的索引实现按键与数组位置的映射,完全替代HashMap的动态哈希表结构。
基础实现(无额外依赖)
use winit::keyboard::KeyCode; // 编译期确定按键总数,直接使用winit提供的VARIANTS长度 const KEY_TOTAL: usize = KeyCode::VARIANTS.len(); struct InputState { key_states: [bool; KEY_TOTAL], } impl InputState { // 初始化所有按键状态为false pub fn new() -> Self { Self { key_states: [false; KEY_TOTAL], } } // 获取指定按键的按下状态 pub fn is_pressed(&self, key: KeyCode) -> bool { // 从VARIANTS中查找按键对应的索引 match KeyCode::VARIANTS.iter().position(|&k| k == key) { Some(idx) => self.key_states[idx], None => false, // 理论上不会触发,所有KeyCode都在VARIANTS中 } } // 更新指定按键的状态 pub fn set_pressed(&mut self, key: KeyCode, pressed: bool) { if let Some(idx) = KeyCode::VARIANTS.iter().position(|&k| k == key) { self.key_states[idx] = pressed; } } }
性能优化(预建索引映射)
如果担心每次position查找的开销,可以预构建一次KeyCode到索引的映射,后续直接通过映射快速访问:
use winit::keyboard::KeyCode; use std::collections::HashMap; use once_cell::sync::Lazy; const KEY_TOTAL: usize = KeyCode::VARIANTS.len(); // 静态懒加载的索引映射,程序启动时仅初始化一次 static KEY_INDEX_MAP: Lazy<HashMap<KeyCode, usize>> = Lazy::new(|| { KeyCode::VARIANTS .iter() .enumerate() .map(|(idx, &key)| (key, idx)) .collect() }); struct InputState { key_states: [bool; KEY_TOTAL], } impl InputState { pub fn new() -> Self { Self { key_states: [false; KEY_TOTAL] } } pub fn is_pressed(&self, key: KeyCode) -> bool { self.key_states[KEY_INDEX_MAP[&key]] } pub fn set_pressed(&mut self, key: KeyCode, pressed: bool) { self.key_states[KEY_INDEX_MAP[&key]] = pressed; } }
注:此方案需要依赖once_cell crate来实现静态懒加载。
为什么不用enum_map?
enum_map要求枚举实现其Enum trait,但KeyCode定义在winit crate内部,受限于Rust的孤儿规则,我们无法为外部枚举手动实现该trait,因此这种方案不可行。
内容的提问来源于stack exchange,提问作者Mateusz Kolpa
相关产品推荐
相关产品推荐

