面试题答案
一键面试使用Rust函数指针实现复杂事件驱动系统
- 定义事件源和事件处理函数类型
// 定义事件源的标识
type EventSourceId = u32;
// 定义事件处理函数类型
type EventHandler = fn();
// 事件驱动系统结构体
struct EventDrivenSystem {
event_handlers: std::collections::HashMap<EventSourceId, Vec<EventHandler>>,
}
- 实现注册和注销事件处理函数的方法
impl EventDrivenSystem {
fn new() -> Self {
EventDrivenSystem {
event_handlers: std::collections::HashMap::new(),
}
}
fn register_handler(&mut self, source_id: EventSourceId, handler: EventHandler) {
self.event_handlers.entry(source_id).or_insert_with(Vec::new).push(handler);
}
fn unregister_handler(&mut self, source_id: EventSourceId, handler: EventHandler) {
if let Some(handlers) = self.event_handlers.get_mut(&source_id) {
handlers.retain(|h| h != &&handler);
}
}
fn trigger_event(&self, source_id: EventSourceId) {
if let Some(handlers) = self.event_handlers.get(&source_id) {
for handler in handlers {
handler();
}
}
}
}
- 示例使用
fn main() {
let mut system = EventDrivenSystem::new();
// 定义事件处理函数
fn handler1() {
println!("Handler 1 triggered");
}
fn handler2() {
println!("Handler 2 triggered");
}
// 注册事件处理函数
system.register_handler(1, handler1);
system.register_handler(1, handler2);
// 触发事件
system.trigger_event(1);
// 注销事件处理函数
system.unregister_handler(1, handler1);
system.trigger_event(1);
}
性能优化
- 减少内存分配
- 使用固定大小的数组:如果已知事件源数量或事件处理函数数量的上限,可以使用固定大小的数组来代替
HashMap
和Vec
。例如,如果最多有100个事件源,并且每个事件源最多有10个处理函数,可以这样定义:
- 使用固定大小的数组:如果已知事件源数量或事件处理函数数量的上限,可以使用固定大小的数组来代替
const MAX_EVENT_SOURCES: usize = 100;
const MAX_HANDLERS_PER_SOURCE: usize = 10;
struct EventDrivenSystem {
event_handlers: [[Option<EventHandler>; MAX_HANDLERS_PER_SOURCE]; MAX_EVENT_SOURCES],
}
- 预先分配内存:在
HashMap
和Vec
使用之前,预先分配足够的内存。例如,在EventDrivenSystem
的new
方法中,如果预计会有大量事件源和处理函数,可以这样预先分配:
impl EventDrivenSystem {
fn new() -> Self {
let mut event_handlers = std::collections::HashMap::with_capacity(1000);
for _ in 0..1000 {
event_handlers.insert(0, Vec::with_capacity(10));
}
EventDrivenSystem {
event_handlers,
}
}
}
- 避免不必要的动态分发
- 使用静态分发:在Rust中,
fn
类型的函数指针是静态分发的,不需要像Box<dyn Fn()>
那样进行动态分发。这意味着在编译时就确定了调用哪个函数,而不是在运行时。因此,通过使用fn
类型(函数指针)而不是trait对象(如Box<dyn Fn()>
),已经在一定程度上避免了不必要的动态分发。
- 使用静态分发:在Rust中,
通过上述方法,可以在一定程度上优化复杂事件驱动系统中函数指针的使用,提高性能。