Как я могу вернуть что-то осмысленное из универсальной функции, если нечего возвращать?
Я строю библиотеку в Русте, которая имеет send
метод, который выполняет HTTP-запросы к локальному серверу RPC, используя reqwest.
Этот метод возвращает универсальный тип R
в Result
где R: DeserializeOwned
, После создания правильных типов для каждого ответа, serde_json::from_str()
можете получить мне тип.
Если нет ответа на запрос, как я могу сделать send
еще вернуть что-нибудь осмысленное?
Вот код, который у меня есть сейчас:
fn send<R, T>(
&self,
request: &RpcRequest<T>,
) -> Result<R, ApiError>
where
T: Serialize + Debug,
R: DeserializeOwned + Debug,
let res = serde_json::from_str(&buf).map_err(|err| ClientError::Json(err))
Теперь я вынужден создать и вернуть Err
, но технически, запрос, не возвращающий ответ, является ожидаемым поведением, поэтому я хочу вернуть что-то кроме Err
,
Я пытался обойти это, завернув R
с Option
, но это означает, что я должен дважды развернуть каждый ответ, и 98% ответов от reqwest действительно содержат данные в своем ответе, поэтому это выглядит как избыточное убийство.
Я тоже пытался вернуть самодельный EmptyResponse
типа, но компилятор жалуется: expected type R, found type EmptyResponse
, Думаю вернуть тип EmptyResponse
было бы то, что я хочу, но, возможно, кто-то может пролить несколько советов о том, как, возможно, сделать это еще лучше.
2 ответа
Прагматичный ответ состоит в том, чтобы иметь две функции:
fn send<R, T>(&self, request: &RpcRequest<T>) -> Result<R, ApiError>
where
T: Serialize + Debug,
R: DeserializeOwned + Debug,
fn send_no_response<T>(&self, request: &RpcRequest<T>) -> Result<(), ApiError>
where
T: Serialize + Debug,
Если ваш сервер возвращает значение, которое можно десериализовать в тип ()
, тогда вы можете избежать накладных расходов двух функций. Однако это не относится к JSON, одному из самых распространенных форматов:
use serde::de::DeserializeOwned; // 1.0.85
use serde_json; // 1.0.37
type Error = Box<std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
fn send<R>() -> Result<R, Error>
where
R: DeserializeOwned,
{
serde_json::from_str("").map_err(Into::into)
}
fn main() {
let _r: () = send().expect("Unable to deserialize");
}
Это паника:
Unable to deserialize: Error("EOF while parsing a value", line: 1, column: 0)
В мире со специализацией вы можете использовать его и черту помощника, чтобы вернуться к одной функции:
#![feature(specialization)]
use serde::de::DeserializeOwned; // 1.0.85
use serde_json; // 1.0.37
type Error = Box<std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
type ApiResponse = &'static str;
trait FromApi: Sized {
fn convert(response: ApiResponse) -> Result<Self, Error>;
}
impl<R> FromApi for R
where
R: DeserializeOwned,
{
default fn convert(response: ApiResponse) -> Result<R, Error> {
eprintln!("deserializing the response");
serde_json::from_str(response).map_err(Into::into)
}
}
impl FromApi for () {
fn convert(_response: ApiResponse) -> Result<Self, Error> {
eprintln!("Ignoring the response");
Ok(())
}
}
fn send<R: FromApi>() -> Result<R> {
eprintln!(r#""sending" the request"#);
let api_response = "";
R::convert(api_response)
}
fn main() {
let _r: () = send().expect("Unable to deserialize");
}
Вы можете вернуть Result<Option<R>, ApiError>
как показано в документации, затем сопоставьте его следующим образом:
match sender.send(request) {
Ok(Some(r)) => {
// process response
}
Ok(None) => {
// process empty response
}
Err(e) => {
// process error
}
}
// or
if let Ok(Some(r)) = sender.send(request) {
// process response
}
Я пытался обойти это, завернув
R
сOption
, но это означает, что я должен дважды развернуть каждый ответ, и 98% ответов от reqwest действительно содержат данные в своем ответе, поэтому это выглядит как избыточное убийство.
Развертывание Option
это очень дешевая операция, не о чем беспокоиться.