Я думаю, что это связано с правилами того, как impl trait
непрозрачные типы фиксируют время жизни.
Если в аргументе T
есть времена жизни, то impl trait
должен включать их.Дополнительные сроки жизни в сигнатуре типа следуют обычным правилам.
Для получения дополнительной информации см .:
Более полный ответ
Исходная цель: функция send_form принимаетвходной параметр типа & T, который отображается в двоичном представлении.Это двоичное представление принадлежит конечному значению Future, и от оригинала & T не осталось никакого остатка.Следовательно, срок службы & T не должен переживать существующую черту.Все хорошо.
Проблема возникает, когда сам T, кроме того, содержит ссылки с временами жизни.Если бы мы не использовали impl Trait, наша подпись выглядела бы примерно так:
fn send_form<T>(self, data: &T) -> SendFormFuture;
И, взглянув на SendFormFuture
, мы можем легко заметить, что там вообще нет остатка T
,Следовательно, даже если у T
есть собственные времена жизни, с которыми мы имеем дело, мы знаем, что все ссылки используются в теле send_form и никогда не используются снова после SendFormFuture
.
Однако с impl Future
в качестве вывода мы не получаем таких гарантий.Нет никакого способа узнать, действительно ли конкретная реализация Future относится к T
.
В случае, когда T
не имеет ссылок, это все еще не является проблемой.Либо impl Future
ссылается на T
и полностью вступает во владение им, либо он не ссылается на него, и никаких проблем со временем жизни не возникает.
Однако, если T
имеет ссылки, вы могли быв конечном итоге в ситуации, когда бетон impl Future
удерживает ссылку, хранящуюся в T
.Даже если impl Future
владеет самим T
, он не владеет значениями, на которые ссылается T
.
Вот почему проверка заимствования должна быть консервативной и настаивать на том, чтобылюбые ссылки внутри T
должны иметь 'static
время жизни.
Единственный обходной путь, который я вижу, это обойти impl Future
и быть явным в типе возвращаемого значения.Затем вы можете легко продемонстрировать контролеру заимствований, что тип вывода вообще не ссылается на тип ввода T
, и любые ссылки в нем не имеют значения.
Исходный код в веб-клиенте actix дляsend_form
выглядит следующим образом:
https://docs.rs/awc/0.2.1/src/awc/request.rs.html#503-522
pub fn send_form<T: Serialize>(
self,
value: &T,
) -> impl Future<
Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
Error = SendRequestError,
> {
let body = match serde_urlencoded::to_string(value) {
Ok(body) => body,
Err(e) => return Either::A(err(Error::from(e).into())),
};
// set content-type
let slf = self.set_header_if_none(
header::CONTENT_TYPE,
"application/x-www-form-urlencoded",
);
Either::B(slf.send_body(Body::Bytes(Bytes::from(body))))
}
Возможно, вам придется исправить библиотеку или написать собственную функцию, которая выполняет то же самое, но с конкретным типом.Если кто-нибудь еще знает, как справиться с этим очевидным ограничением impl trait
, я бы с удовольствием его услышал.
Вот как далеко я продвинулся при переписывании send_form
в awc
(actix-веб-клиентская библиотека):
pub fn send_form_alt<T: Serialize>(
self,
value: &T,
// ) -> impl Future<
// Item = ClientResponse<impl Stream<Item = Bytes, Error = PayloadError>>,
// Error = SendRequestError,
) -> Either<
FutureResult<String, actix_http::error::Error>,
impl Future<
Item = crate::response::ClientResponse<impl futures::stream::Stream>,
Error = SendRequestError,
>,
> {
Некоторые оговорки:
Either::B
обязательно непрозрачный impl trait
из Future
. - Первый параметр
FutureResult
может быть на самом деле Void
или как там называется эквивалент Void в Rust.