阻止 Rust 对错误类型强制执行 serde::Deserialize 特征
Stop Rust from enforcing the serde::Deserialize trait on an error type
下面的代码是我正在编写的一个与网络对话的小型库的开始 API。库的用户将实例化一个客户端 MyClient
并通过它访问网络 API。在这里,我试图在向它发出请求之前从 API 获取访问令牌。
在 get_new_access()
中,我可以发出请求并收到 JSON 响应。然后我尝试使用 serde 将响应转换为 Access
结构,这就是问题开始的地方。
我已经创建了一个特定于库的错误枚举 MyError
,它可以表示 get_new_access()
中可能发生的 JSON 反序列化和 reqwest 错误。但是,当我去编译时,我得到 the trait serde::Deserialize<'_> is not implemented for MyError
。我的理解是,发生这种情况是因为在我遇到上述错误之一的情况下,serde 不知道如何将其反序列化为 Access
结构。当然,我根本不希望它这样做,所以我的问题是我应该怎么做?
我查看了各种 serde 反序列化示例,但它们似乎都假设它们是 运行 在只能 return serde 错误的主函数中。如果我将 #[derive(Deserialize)]
放在 MyError
的声明之上,那么我会得到同样的错误,但它会转移到 reqwest::Error
和 serde_json::Error
。
use std::error;
use std::fmt;
extern crate chrono;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use chrono::prelude::*;
use reqwest::Client;
pub struct MyClient {
access: Access,
token_expires: DateTime<Utc>,
}
#[derive(Deserialize, Debug)]
struct Access {
access_token: String,
expires_in: i64,
token_type: String,
}
fn main() {
let sc: MyClient = MyClient::new();
println!("{:?}", &sc.access);
}
impl MyClient {
pub fn new() -> MyClient {
let a: Access = MyClient::get_new_access().expect("Couldn't get Access");
let e: DateTime<Utc> = chrono::Utc::now(); //TODO
MyClient {
access: a,
token_expires: e,
}
}
fn get_new_access() -> Result<Access, MyError> {
let params = ["test"];
let client = Client::new();
let json = client
.post(&[""].concat())
.form(¶ms)
.send()?
.text()
.expect("Couldn't get JSON Response");
println!("{}", &json);
serde_json::from_str(&json)?
//let a = Access {access_token: "Test".to_string(), expires_in: 3600, token_type: "Test".to_string() };
//serde_json::from_str(&json)?
}
}
#[derive(Debug)]
pub enum MyError {
WebRequestError(reqwest::Error),
ParseError(serde_json::Error),
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "eRROR")
}
}
impl error::Error for MyError {
fn description(&self) -> &str {
"API internal error"
}
fn cause(&self) -> Option<&error::Error> {
// Generic error, underlying cause isn't tracked.
None
}
}
impl From<serde_json::Error> for MyError {
fn from(e: serde_json::Error) -> Self {
MyError::ParseError(e)
}
}
impl From<reqwest::Error> for MyError {
fn from(e: reqwest::Error) -> Self {
MyError::WebRequestError(e)
}
}
游乐场linkhere.
您的第一个问题是您的 fn get_new_access() -> Result<Access, MyError>
期望 Result
。但在这里:
//...
serde_json::from_str(&json)?
}
因为使用 ?
(try macro),你正在尝试 return Result
的展开值,它是一个子类型serde::Deserialize<'_>
。编译器会警告您 Deserialize
不是 Result
。你应该做的只是 return 结果而不打开它:
//...
serde_json::from_str(&json)
}
或
//...
let access = serde_json::from_str(&json)?; // gets access or propagates error
Ok(access) //if no error return access in a Result
}
然后你会遇到第二个问题,因为你的函数在 Result
中期望 MyError
而你正在 returning serde_json::Error
这个调用 serde_json::from_str(&json)
.幸运的是 Result
有函数 map_err
将实际错误类型映射到您的自定义错误类型。
此代码将解决您的问题:
//...
serde_json::from_str(&json).map_err(MyError::ParseError)
}
对于 中的请求:
For example, if I change the web request line to let json = client.post("").form(¶ms).send().map_err(MyError::WebRequestError)?.text()?;
,
is that better practice at all?
是的,但是由于 text()
return 是 Result
,您也需要将其错误映射为 MyError
。由于 send
和 text
具有相同的错误类型(reqwest::Error
),您可以将结果与 and_then
结合起来:
let json = client
.post(&[""].concat())
.form(¶ms)
.send()
.and_then(Response::text) //use reqwest::Response;
.map_err(MyError::WebRequestError)?;
下面的代码是我正在编写的一个与网络对话的小型库的开始 API。库的用户将实例化一个客户端 MyClient
并通过它访问网络 API。在这里,我试图在向它发出请求之前从 API 获取访问令牌。
在 get_new_access()
中,我可以发出请求并收到 JSON 响应。然后我尝试使用 serde 将响应转换为 Access
结构,这就是问题开始的地方。
我已经创建了一个特定于库的错误枚举 MyError
,它可以表示 get_new_access()
中可能发生的 JSON 反序列化和 reqwest 错误。但是,当我去编译时,我得到 the trait serde::Deserialize<'_> is not implemented for MyError
。我的理解是,发生这种情况是因为在我遇到上述错误之一的情况下,serde 不知道如何将其反序列化为 Access
结构。当然,我根本不希望它这样做,所以我的问题是我应该怎么做?
我查看了各种 serde 反序列化示例,但它们似乎都假设它们是 运行 在只能 return serde 错误的主函数中。如果我将 #[derive(Deserialize)]
放在 MyError
的声明之上,那么我会得到同样的错误,但它会转移到 reqwest::Error
和 serde_json::Error
。
use std::error;
use std::fmt;
extern crate chrono;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use chrono::prelude::*;
use reqwest::Client;
pub struct MyClient {
access: Access,
token_expires: DateTime<Utc>,
}
#[derive(Deserialize, Debug)]
struct Access {
access_token: String,
expires_in: i64,
token_type: String,
}
fn main() {
let sc: MyClient = MyClient::new();
println!("{:?}", &sc.access);
}
impl MyClient {
pub fn new() -> MyClient {
let a: Access = MyClient::get_new_access().expect("Couldn't get Access");
let e: DateTime<Utc> = chrono::Utc::now(); //TODO
MyClient {
access: a,
token_expires: e,
}
}
fn get_new_access() -> Result<Access, MyError> {
let params = ["test"];
let client = Client::new();
let json = client
.post(&[""].concat())
.form(¶ms)
.send()?
.text()
.expect("Couldn't get JSON Response");
println!("{}", &json);
serde_json::from_str(&json)?
//let a = Access {access_token: "Test".to_string(), expires_in: 3600, token_type: "Test".to_string() };
//serde_json::from_str(&json)?
}
}
#[derive(Debug)]
pub enum MyError {
WebRequestError(reqwest::Error),
ParseError(serde_json::Error),
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "eRROR")
}
}
impl error::Error for MyError {
fn description(&self) -> &str {
"API internal error"
}
fn cause(&self) -> Option<&error::Error> {
// Generic error, underlying cause isn't tracked.
None
}
}
impl From<serde_json::Error> for MyError {
fn from(e: serde_json::Error) -> Self {
MyError::ParseError(e)
}
}
impl From<reqwest::Error> for MyError {
fn from(e: reqwest::Error) -> Self {
MyError::WebRequestError(e)
}
}
游乐场linkhere.
您的第一个问题是您的 fn get_new_access() -> Result<Access, MyError>
期望 Result
。但在这里:
//...
serde_json::from_str(&json)?
}
因为使用 ?
(try macro),你正在尝试 return Result
的展开值,它是一个子类型serde::Deserialize<'_>
。编译器会警告您 Deserialize
不是 Result
。你应该做的只是 return 结果而不打开它:
//...
serde_json::from_str(&json)
}
或
//...
let access = serde_json::from_str(&json)?; // gets access or propagates error
Ok(access) //if no error return access in a Result
}
然后你会遇到第二个问题,因为你的函数在 Result
中期望 MyError
而你正在 returning serde_json::Error
这个调用 serde_json::from_str(&json)
.幸运的是 Result
有函数 map_err
将实际错误类型映射到您的自定义错误类型。
此代码将解决您的问题:
//...
serde_json::from_str(&json).map_err(MyError::ParseError)
}
对于
For example, if I change the web request line to
let json = client.post("").form(¶ms).send().map_err(MyError::WebRequestError)?.text()?;
, is that better practice at all?
是的,但是由于 text()
return 是 Result
,您也需要将其错误映射为 MyError
。由于 send
和 text
具有相同的错误类型(reqwest::Error
),您可以将结果与 and_then
结合起来:
let json = client
.post(&[""].concat())
.form(¶ms)
.send()
.and_then(Response::text) //use reqwest::Response;
.map_err(MyError::WebRequestError)?;