面试题答案
一键面试实现方案
- 定义错误类型:为每个任务可能出现的错误定义相应的错误类型。这些错误类型可以通过
enum
来定义,并实现std::error::Error
trait。 - 使用
Result
枚举:在异步函数的返回值中使用Result
枚举,其中Ok
包含成功时的数据,Err
包含错误类型。 - 异步任务封装:将每个任务封装在一个异步函数中,该函数接受所需的参数并返回
Result
。 - 并发执行任务:使用
tokio
等异步运行时库来并发执行这些异步任务。可以使用tokio::join!
宏或者futures::future::join_all
函数。 - 错误处理优化:避免在错误处理中出现不必要的等待。如果一个任务出错,不需要等待其他任务完成,可以提前返回错误。
代码示例
use std::error::Error;
use tokio;
// 定义任务1的错误类型
#[derive(Debug)]
struct Task1Error;
impl std::fmt::Display for Task1Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Task1 error")
}
}
impl Error for Task1Error {}
// 定义任务2的错误类型
#[derive(Debug)]
struct Task2Error;
impl std::fmt::Display for Task2Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Task2 error")
}
}
impl Error for Task2Error {}
// 任务1的结构体和异步函数
struct Task1;
impl Task1 {
async fn run(&self) -> Result<i32, Task1Error> {
// 模拟一些异步操作
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
// 这里简单返回一个成功值,实际可能有更多业务逻辑
Ok(42)
}
}
// 任务2的结构体和异步函数
struct Task2;
impl Task2 {
async fn run(&self) -> Result<String, Task2Error> {
// 模拟一些异步操作
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
// 这里简单返回一个成功值,实际可能有更多业务逻辑
Ok("Hello, world!".to_string())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let task1 = Task1;
let task2 = Task2;
let result = tokio::try_join!(task1.run(), task2.run());
match result {
Ok((task1_result, task2_result)) => {
println!("Task1 result: {}", task1_result);
println!("Task2 result: {}", task2_result);
Ok(())
}
Err(e) => {
eprintln!("Error: {}", e);
Err(Box::new(e))
}
}
}
在上述代码中:
- 定义了
Task1Error
和Task2Error
分别表示任务1和任务2可能出现的错误类型,并实现了Error
trait。 Task1
和Task2
结构体封装了各自的业务逻辑,run
方法是异步函数,返回Result
。- 在
main
函数中,使用tokio::try_join!
宏并发执行两个任务。如果其中任何一个任务出错,try_join!
会立即返回错误,避免了不必要的等待。