是否有一种相当简单的方法来处理 Rust 中 gRPC 服务器处理函数的错误转换?我是 Rust 新手,所以也许这是不可能的,但我确实看到很多东西表明错误类型之间的转换非常常见。
例如:
我有以下处理程序:
use tonic::{Request, Response, Status}
use stuff::{DoStuffRequest, stuff_server::Stuff}
#[derive(Default)]
pub struct MyStuff {}
#[tonic::async_trait]
impl Stuff for MyStuff {
async fn do_stuff(&self, request: Request<DoStuffRequest>) ->
Response<DoStuffResponse, Status>
{
let data = ...; // get what should be valid json
let jsonstr = String::from_utf8(data)?;
let json = serde_json::from_str(&jsonstr)?;
Ok(Response::new(DoStuffResponse{...}))
}
}
我想要的是
?
从from_utf8
和from_str
中获取任何错误并将它们转换为Status
,这样我就不必使用大的match
表达式来转换错误。
我正在使用
thiserror
创建中间错误类型,然后从我的错误类型转换为 Status
,但 rust 似乎不想转换它。
#[derive(Error, Debug)]
enum MyStuffError {
#[error("UTF-8 conversion error: {0}")]
Utf8Error(#[from] std::string::FromUtf8Error),
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error)
}
impl From<MyStuffError> for Status {
fn from(error: MyStuffError) -> Self {
Status::unknown(error.to_string())
}
}
我得到:
error[E0277]: `?` couldn't convert the error to `Status`
--> src/server.rs:58:48
|
58 | let jsonstr = String::from_utf8(data)?;
| ^ the trait `From<FromUtf8Error>` is not implemented for `Status`, which is required by `Result<tonic::Response<DoStuffResponse>, Status>: FromResidual<Result<Infallible, FromUtf8Error>>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
<Status as From<MyStuffError>>
<Status as From<h2::error::Error>>
<Status as From<std::io::Error>>
= note: required for `Result<tonic::Response<DoStuffResponse>, Status>` to implement `FromResidual<Result<Infallible, FromUtf8Error>>`
?
到
E
的直接转换时,
Status
才能转换错误。它不能做 E
→ E1
→ Status
,因为它不知道选择哪个 E1
(毕竟可能有几种类型可以去那里)。一种解决方案是使用 map_err
指定正确的类型,例如:
#[tonic::async_trait]
impl Stuff for MyStuff {
async fn do_stuff(&self, request: Request<DoStuffRequest>) ->
Result<Response<DoStuffResponse>, Status>
{
let data = ...; // get what should be valid json
let jsonstr = String::from_utf8 (data).map_err (MyStuffError::from)?;
let json = serde_json::from_str (&jsonstr).map_err (MyStuffError::from)?;
Ok (Response::new (DoStuffResponse {…}))
}
}