MST
星途 面试题库

面试题:Rust Fn trait自定义实现在复杂场景下的应用

考虑一个场景,有多个不同类型的自定义结构体,每个结构体都实现了`Fn` trait。现在要创建一个统一的调度器,它可以接受不同类型但都实现了`Fn` trait的实例,并根据运行时的条件来决定调用哪个实例。请设计并实现这个调度器,包括如何处理不同类型的`Fn`实例的存储、调度逻辑以及错误处理机制。要求代码具有良好的可读性和扩展性,并说明设计思路。
39.8万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 存储不同类型的 Fn 实例:使用 Box<dyn Fn()> 来存储不同类型但都实现了 Fn trait 的实例。Box<dyn Trait> 这种形式可以实现动态分发,允许我们在运行时处理不同类型的对象。
  2. 调度逻辑:在调度器中,我们可以使用一个 Vec<Box<dyn Fn()>> 来存储这些实例。根据运行时的条件,我们从这个向量中选择合适的实例并调用它。
  3. 错误处理机制:由于 Fn trait 本身不支持返回错误,我们可以通过包裹闭包,将错误处理逻辑添加进去。例如,使用 Result 类型来处理可能的错误。

代码实现

use std::fmt::Debug;

// 调度器结构体
struct Scheduler {
    handlers: Vec<Box<dyn Fn() -> Result<(), String>>>,
}

impl Scheduler {
    // 添加处理函数
    fn add_handler<F>(&mut self, handler: F)
    where
        F: 'static + Fn() -> Result<(), String>,
    {
        self.handlers.push(Box::new(handler));
    }

    // 根据索引调用处理函数
    fn execute(&self, index: usize) -> Result<(), String> {
        if index >= self.handlers.len() {
            return Err("Index out of bounds".to_string());
        }
        (self.handlers[index])()
    }
}

// 示例自定义结构体
struct Handler1;
impl Handler1 {
    fn new() -> Self {
        Handler1
    }
}
impl Fn() -> Result<(), String> for Handler1 {
    fn call(&self) -> Result<(), String> {
        println!("Handler1 executed");
        Ok(())
    }
}

struct Handler2;
impl Handler2 {
    fn new() -> Self {
        Handler2
    }
}
impl Fn() -> Result<(), String> for Handler2 {
    fn call(&self) -> Result<(), String> {
        println!("Handler2 executed");
        Ok(())
    }
}

fn main() {
    let mut scheduler = Scheduler { handlers: Vec::new() };

    scheduler.add_handler(Handler1::new());
    scheduler.add_handler(Handler2::new());

    match scheduler.execute(0) {
        Ok(_) => (),
        Err(e) => eprintln!("Error: {}", e),
    }
}

代码说明

  1. Scheduler 结构体:包含一个 Vec<Box<dyn Fn() -> Result<(), String>>> 类型的 handlers 字段,用于存储不同类型的处理函数。
  2. add_handler 方法:用于向调度器中添加处理函数。这里使用了泛型 F,并约束 F 实现 Fn() -> Result<(), String> 以及具有 'static 生命周期。
  3. execute 方法:根据传入的索引,调用对应的处理函数。如果索引超出范围,返回错误。
  4. 自定义结构体 Handler1Handler2:都实现了 Fn() -> Result<(), String> trait,模拟不同类型的处理函数。在 main 函数中,创建调度器实例,添加处理函数,并执行其中一个处理函数。错误处理通过 Result 类型来实现。这样设计使得代码具有良好的可读性和扩展性,便于后续添加更多类型的处理函数。