gpt4 book ai didi

rust - 阻止 Rust 对错误类型强制执行 serde::Deserialize 特性

转载 作者:行者123 更新时间:2023-11-29 07:52:48 25 4
gpt4 key购买 nike

下面的代码是我正在编写的用于与 Web API 对话的小型库的开始部分。库的用户将实例化一个客户端 MyClient并通过它访问 Web API。在这里,我尝试在向 API 发出请求之前从 API 获取访问 token 。

get_new_access()我能够发出请求并接收 JSON 响应。然后我尝试使用 serde 将响应转换为 Access结构,这就是问题开始的地方。

我创建了一个特定于库的错误枚举 MyError这可以表示可能在 get_new_access() 中发生的 JSON 反序列化和 reqwest 错误.但是,当我去编译时,我得到 the trait serde::Deserialize<'_> is not implemented for MyError .我的理解是这种情况正在发生,因为在我遇到上述错误之一的情况下,serde 不知道如何将其反序列化为 Access。结构。当然,我根本不希望它这样做,所以我的问题是我应该怎么做?

我看过各种 serde 反序列化示例,但它们似乎都假设它们在只能返回 serde 错误的主函数中运行。如果我把 #[derive(Deserialize)]以上MyError的声明,然后我得到同样的错误,但它转移到 reqwest::Errorserde_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(&params)
.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)
}
}

Playground 链接 here .

最佳答案

您的第一个问题是您的 fn get_new_access() -> Result<Access, MyError>期望一个 Result .但是在这里:

    //...
serde_json::from_str(&json)?
}

因为使用 ? (尝试宏),你正试图返回 Result的展开值,它是 serde::Deserialize<'_> 的子类型.编译器会就此 Deserialize 向您发出警告不是 Result 。你应该做的只是返回结果而不打开它:

    //...
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
}

然后你会遇到第二个问题,因为你的函数需要 MyErrorResult当你回来的时候serde_json::Error通过此电话 serde_json::from_str(&json) .幸好Result具有函数map_err它将实际错误类型映射到您的自定义错误类型。

此代码将解决您的问题:

    //...
serde_json::from_str(&json).map_err(MyError::ParseError)
}

对于 comment 中的请求:

For example, if I change the web request line to let json = client.post("").form(&params).send().map_err(MyError::WebRequestError)?.text()?;, is that better practice at all?

是的,但是自 text()返回 Result您需要将其错误映射为 MyError也。由于两者 sendtext具有相同的错误类型 ( reqwest::Error ) 您可以将结果与 and_then 合并:

let json = client
.post(&[""].concat())
.form(&params)
.send()
.and_then(Response::text) //use reqwest::Response;
.map_err(MyError::WebRequestError)?;

关于rust - 阻止 Rust 对错误类型强制执行 serde::Deserialize 特性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53897618/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com