在actix-web服务中返回anyhow::Result遇错误,求解决方案
解决Actix-Web中使用
anyhow::Result时的错误 问题说明
尝试在Actix-Web服务中用anyhow::Result替代std::io::Result,利用?运算符简化错误处理,但编译时触发E0277错误,提示无法将r2d2::Error转换为std::io::Error。
错误信息
error[E0277]: `?` couldn't convert the error to `std::io::Error` --> backend/src/services/get_movies_count.rs:10:48 | 10 | let mut database = app_state.database.get()?; | ^ the trait `std::convert::From<r2d2::Error>` is not implemented for `std::io::Error` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `std::convert::From<T>`: <std::io::Error as std::convert::From<ErrorKind>> <std::io::Error as std::convert::From<IntoInnerError<W>>> <std::io::Error as std::convert::From<JoinError>> <std::io::Error as std::convert::From<NulError>> <std::io::Error as std::convert::From<flate2::mem::CompressError>> <std::io::Error as std::convert::From<flate2::mem::DecompressError>> <std::io::Error as std::convert::From<getrandom::error::Error>> <std::io::Error as std::convert::From<httpdate::Error>> and 4 others = note: required for `Result<String, std::io::Error>` to implement `FromResidual<Result<Infallible, r2d2::Error>>` For more information about this error, try `rustc --explain E0277`.
相关代码
服务代码(get_movies_count.rs)
use crate::AppState; use anyhow::Result; use actix_web::{get, web::Data}; #[get("movies/count")] async fn get_movies_count(app_state: Data<AppState>) -> Result<String> { let database = app_state.database.get()?; Ok("Hi".into()) }
主文件代码(main.rs)
use std::env::var; use anyhow::Result; use actix_web::{HttpServer, App, web::Data}; use diesel::{r2d2::{ConnectionManager, Pool}, SqliteConnection}; use services::{ get_movies_count::get_movies_count, }; mod schema; mod models; mod services; #[derive(Clone)] struct AppState { database: Pool<ConnectionManager<SqliteConnection>> } #[actix_web::main] async fn main() -> Result<()> { dotenv::dotenv()?; let manager = ConnectionManager::<SqliteConnection>::new(var("DATABASE_URL")?); let pool = Pool::builder().build(manager)?; let app_state = AppState { database: pool }; HttpServer::new( move || { App::new() .app_data(Data::new(app_state.clone())) .service(get_movies_count) } ) .bind( ( var("HOST")?, var("API_PORT")?.parse()? ) )? .run() .await?; Ok(()) }
解决方案
问题核心是Actix-Web默认会将路由返回的anyhow::Result尝试转换为std::io::Error,但r2d2::Error未实现对应转换的From trait。以下是三种可行解决方式:
方案1:改用actix_web::Error作为返回错误类型
修改路由函数返回类型为Result<String, actix_web::Error>,利用框架自带的错误转换能力:
use crate::AppState; use anyhow::Result; use actix_web::{get, web::Data, Error}; #[get("movies/count")] async fn get_movies_count(app_state: Data<AppState>) -> Result<String, Error> { let database = app_state.database.get()?; Ok("Hi".into()) }
如果需要自定义错误信息,可结合anyhow::Context包装错误:
use crate::AppState; use anyhow::{Context, Result}; use actix_web::{get, web::Data, error::ErrorInternalServerError}; #[get("movies/count")] async fn get_movies_count(app_state: Data<AppState>) -> Result<String, actix_web::Error> { let database = app_state.database.get() .context("获取数据库连接失败")? .map_err(ErrorInternalServerError); Ok("Hi".into()) }
方案2:手动实现r2d2::Error到std::io::Error的转换
若坚持使用std::io::Error,可手动实现From trait:
use std::io; use r2d2::Error; impl From<Error> for io::Error { fn from(err: Error) -> Self { io::Error::new(io::ErrorKind::Other, err.to_string()) } }
将这段代码放在项目的公共模块(如main.rs或单独的错误处理模块)中,?运算符即可自动完成转换。
方案3:使用actix-web-anyhow crate适配anyhow
该 crate 专门为Actix-Web提供anyhow支持:
- 在
Cargo.toml添加依赖:
actix-web-anyhow = "0.1"
- 修改路由函数:
use crate::AppState; use anyhow::Result; use actix_web::{get, web::Data}; use actix_web_anyhow::ResponseError; #[get("movies/count")] async fn get_movies_count(app_state: Data<AppState>) -> Result<String> { let database = app_state.database.get()?; Ok("Hi".into()) }
这样anyhow::Result会被自动转换为符合HTTP规范的响应错误。
内容的提问来源于stack exchange,提问作者VenoX
相关产品推荐
相关产品推荐

