Ограничение черты `(): futures::Future` не выполняется при использовании TcpConnectionNew
Я пытаюсь написать простой TCP-клиент в Rust, используя Tokio crate. Мой код довольно близок к этому примеру, за исключением TLS:
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use futures::Future;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use tokio_io::io;
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let connection = TcpStream::connect(&"127.0.0.1:8080".parse().unwrap(), &handle);
let server = connection.and_then(|stream| {
io::write_all(stream, b"hello");
});
core.run(server).unwrap();
}
Однако компиляция завершается с ошибкой:
error[E0277]: the trait bound `(): futures::Future` is not satisfied
--> src/main.rs:16:29
|
16 | let server = connection.and_then(|stream| {
| ^^^^^^^^ the trait `futures::Future` is not implemented for `()`
|
= note: required because of the requirements on the impl of `futures::IntoFuture` for `()`
error[E0277]: the trait bound `(): futures::Future` is not satisfied
--> src/main.rs:20:10
|
20 | core.run(server).unwrap();
| ^^^ the trait `futures::Future` is not implemented for `()`
|
= note: required because of the requirements on the impl of `futures::IntoFuture` for `()`
Я нахожу это странным, потому что согласно документации это должно быть реализовано.
я использую
- Ржавчина 1.19.0
- фьючерсы 0.1.16
- Токио-ядро 0.1.10
- Токио-ио 0.1.3
Что мне не хватает?
2 ответа
TL;DR: удалить точку с запятой после io::write_all
,
Просмотрите определение and_then
:
fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F>
where
F: FnOnce(Self::Item) -> B,
B: IntoFuture<Error = Self::Error>,
Self: Sized,
Закрытие (F
) должен вернуть какой-то тип (B
) которые можно превратить в будущее (B: IntoFuture
) с типом ошибки, который соответствует начальному закрытию (Error = Self::Error
).
Что возвращает ваше закрытие? ()
, Это почему? Потому что вы поставили точку с запятой (;
) в конце вашей строки. ()
не реализует черту IntoFuture
, что указывается в части сообщения об ошибке "в значении futures::IntoFuture
за ()
":
impl<F: Future> IntoFuture for F {
type Future = F;
type Item = F::Item;
type Error = F::Error;
}
Удаление точки с запятой приведет к Future
вернулся io::write_all
быть возвращенным обратно and_then
и программа скомпилируется.
В целом, фьючерсы работают путем объединения меньших компонентов, которые сами являются фьючерсами. Все это работает вместе, чтобы построить одно большое будущее, которое по сути является конечным автоматом. Это хорошо иметь в виду, так как вам почти всегда нужно возвращать будущее при использовании таких комбинаторов.
К сожалению, ответ здесь довольно конкретный, но возникает вопрос для любого вида поиска:
черта
futures::Future
не реализовано для()
Типичный сценарий такого рода ошибок:
foo.then(|stream| {
// ... Do random things here
final_statement();
});
Это вызывает ошибку, потому что большинство функций расширения требует реализации возвращаемого типа IntoFuture
, Тем не мение, ()
не реализует IntoFuture
и завершив блок с помощью ;
неявный тип возвращаемого значения ()
,
Тем не мение, IntoFuture
реализован для Option
а также Result
,
Вместо того, чтобы просто произвольно удалять точки с запятой в надежде, что это каким-то образом волшебным образом скомпилирует ваш код, подумайте:
Вы должны возвращать что-то, что может быть преобразовано в Future
с помощью IntoFuture
,
Если у вас нет конкретного обещания, которое вы возвращаете, подумайте о возвращении Ok(())
просто сказать "это сделано" из вашего обратного вызова:
foo.then(|stream| {
// ... Do random things here
final_statement();
return Ok(()); // <-- Result(()) implements `IntoFuture`.
});
Обратите внимание, что я заканчиваю этот блок явным оператором return; это намеренно Это типичный пример того, как эргономика "может опустить точку с запятой для неявного возврата объекта" является ощутимо вредной; завершить блок Ok(());
будет продолжать сбой с той же ошибкой.