问题
I'm trying to wrap my head around futures in Rust but I am confused by this code which is supposed to send messages arriving at rx
to sink
:
extern crate futures;
extern crate tokio_core;
extern crate websocket;
use websocket::message::OwnedMessage;
use websocket::server::InvalidConnection;
use websocket::async::Server;
use tokio_core::reactor::Core;
use futures::{Future, Sink, Stream};
use futures::sync::mpsc;
use std::{thread, time};
use futures::sync::mpsc::Receiver;
fn main() {
let mut core = Core::new().unwrap();
let (mut tx, rx) = mpsc::channel(5);
thread::spawn(|| worker(rx));
let mut i = 0;
loop {
let res = tx.clone().send(OwnedMessage::Text(format!("Test {}", i)));
core.run(res);
i += 1;
let period = time::Duration::from_millis(200);
thread::sleep(period);
}
}
fn worker(rx: Receiver<OwnedMessage>) {
let mut core = Core::new().unwrap();
let handle = core.handle();
// bind to the server
let server = Server::bind("127.0.0.1:9000", &handle).unwrap();
let f = server.incoming()
// we don't wanna save the stream if it drops
.map_err(|InvalidConnection { error, .. }| error)
.for_each(|(upgrade, addr)| {
// accept the request to be a ws connection if it does
let f = upgrade
.use_protocol("rust-websocket")
.accept()
.and_then(|(s, _)| {
let (sink, stream) = s.split();
rx // using stream (echoing back) works
.forward(sink)
.map_err(|error| {
error
})
.and_then(|(a, sink)| {
sink.send(OwnedMessage::Close(None))
})
});
handle.spawn(f.map_err(move |e| println!("Err"))
.map(move |_| println!("Done")));
Ok(())
});
core.run(f).expect("somerror");
}
As noted in the comment, using stream
as input works fine. When using rx
, the compiler complains about a type mismatch regarding the error types (I believe):
error[E0271]: type mismatch resolving `<futures::stream::SplitSink<websocket::client::async::Framed<tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>> as futures::Sink>::SinkError == ()`
--> src/main.rs:47:26
|
47 | .forward(sink)
| ^^^^^^^ expected enum `websocket::WebSocketError`, found ()
|
= note: expected type `websocket::WebSocketError`
found type `()`
error[E0599]: no method named `map_err` found for type `futures::stream::Forward<futures::sync::mpsc::Receiver<websocket::OwnedMessage>, futures::stream::SplitSink<websocket::client::async::Framed<tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>>>` in the current scope
--> src/main.rs:48:26
|
48 | .map_err(|error| {
| ^^^^^^^
|
= note: the method `map_err` exists but the following trait bounds were not satisfied:
`futures::stream::Forward<futures::sync::mpsc::Receiver<websocket::OwnedMessage>, futures::stream::SplitSink<websocket::client::async::Framed<tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>>> : futures::Future`
These are my dependencies:
[dependencies]
websocket = "0.20.0"
futures = "0.1"
tokio-core = "0.1"
What am I missing here?
回答1:
error[E0271]: type mismatch resolving `<futures::stream::SplitSink< websocket::client::async::Framed< tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>> as futures::Sink>::SinkError == ()`
We have two types here: <futures::stream::SplitSink<...> as futures::Sink>::SinkError
and ()
. Where do these two types come from? Also, the first one is an unresolved associated type; perhaps we could resolve it to get some more insight? Let's trace it step by step.
First, we need to figure out why the compiler is trying to match these two types in the first place. If we look at the signature for forward, we'll see the constraint Self::Error: From<S::SinkError>
. Self
is the type of the stream we're calling forward
on, while S
is the type of the sink that's passed as an argument to forward
.
We're calling forward
on rx
, whose type is futures::sync::mpsc::Receiver
. On the documentation page for Receiver, we can see the following:
impl<T> Stream for Receiver<T> type Item = T type Error = ()
This shows us where the ()
came from. Let's look at the sink
argument now.
The type of sink
is futures::stream::SplitSink<websocket::client::async::Framed<tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>>
(we know this from the error message; the RLS also confirms this). On the documentation page for SplitSink, we have:
impl<S: Sink> Sink for SplitSink<S> type SinkItem = S::SinkItem type SinkError = S::SinkError
So SplitSink
's SinkError
is the same as its inner sink's SinkError
. The inner sink's type is websocket::client::async::Framed<tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>
. What does the documentation for Framed say?
impl<T, U> Sink for Framed<T, U> where T: AsyncWrite, U: Encoder, <U as Encoder>::Error: From<Error>, type SinkItem = <U as Encoder>::Item type SinkError = <U as Encoder>::Error
Framed
has two type parameters, but we only need to look at the second one, which is websocket::async::MessageCodec<websocket::OwnedMessage>
here, to determine the SinkError
type. Let's take a look at MessageCodec now. (Note: websocket::codec::ws::MessageCodec
is reexported as websocket::async::MessageCodec
.)
impl<M> Decoder for MessageCodec<M> where M: MessageTrait, type Item = OwnedMessage type Error = WebSocketError
Ah ha! The sink produces errors of type WebSocketError.
Now that we've figured out the types, let's go back to why we cared about the types in the first place. We were trying to understand why the constraint Self::Error: From<S::SinkError>
wasn't met on the call to forward
. We now know that the compiler is trying to resolve (): From<WebSocketError>
. It looks like there's no impl From<WebSocketError> for ()
. Let's verify this:
extern crate websocket;
fn main() {
let a = websocket::result::WebSocketError::NoDataAvailable;
let () = From::from(a);
}
Indeed, this fails to compile:
error[E0277]: the trait bound `(): std::convert::From<websocket::WebSocketError>` is not satisfied
--> src/main.rs:5:14
|
5 | let () = From::from(a);
| ^^^^^^^^^^ the trait `std::convert::From<websocket::WebSocketError>` is not implemented for `()`
|
= note: required by `std::convert::From::from`
We can work around the missing implementation by using sink_map_err to change sink
's error type.
let (sink, stream) = s.split();
let sink = sink.sink_map_err(|_| ()); // <<<<<
rx
.forward(sink)
.and_then(|(a, sink)| {
sink.send(OwnedMessage::Close(None))
})
This solves the call to forward
, but now the result of this closure doesn't compose with upgrade.use_protocol("rust-websocket").accept()
, which still has WebSocketError
as its error type. It makes more sense to change rx
's error type instead. But how do we construct a WebSocketError
from a ()
, which carries no information?
You might be wondering, why does Receiver
use ()
for its error type? If we look at the source code, we can see that in fact, poll
never returns an error. I think it would be more appropriate if the error type was !
(the never type) or some other void type, to clearly indicate that errors are impossible; there's an issue open on futures requesting this change for futures 0.2.
Since errors are impossible, we don't need to construct a WebSocketError
; we can just diverge instead, for example by panicking.
fn worker(rx: Receiver<OwnedMessage>) {
let rx = rx.map_err(|()| panic!("Receiver should never fail!"));
let mut core = Core::new().unwrap();
let handle = core.handle();
// bind to the server
let server = Server::bind("127.0.0.1:9000", &handle).unwrap();
let f = server.incoming()
// we don't wanna save the stream if it drops
.map_err(|InvalidConnection { error, .. }| error)
.for_each(|(upgrade, addr)| {
// accept the request to be a ws connection if it does
let f = upgrade
.use_protocol("rust-websocket")
.accept()
.and_then(|(s, _)| {
let (sink, stream) = s.split();
rx
.forward(sink)
.and_then(|(a, sink)| {
sink.send(OwnedMessage::Close(None))
})
});
handle.spawn(f.map_err(move |e| println!("Err"))
.map(move |_| println!("Done")));
Ok(())
});
core.run(f).expect("somerror");
}
Now, there's still an error:
error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
--> src/main.rs:43:31
|
30 | let rx = rx.map_err(|()| panic!("Receiver should never fail!"));
| -- captured outer variable
...
43 | .and_then(|(s, _)| {
| ^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure
Why is the closure trying to move rx
? Because forward
takes self
by value. Why is the closure an FnMut
? Watch out, Future::and_then requires an FnOnce
(it's valid to move a value from a captured variable into an FnOnce
closure), but Stream::for_each requires an FnMut
. This makes sense: for_each
will invoke the closure once for each incoming connection!
The channels you're using are multi-producer, single-consumer (hence the name mpsc), but you're trying to have multiple consumers here (each connection is trying to read from the receiver). I'll leave it to you to fix this design issue in your program. Remember that there can be multiple concurrent client connections!
来源:https://stackoverflow.com/questions/47582586/type-mismatch-resolving-the-error-type-when-forwarding-messages-from-a-futures-c