在将消息从期货渠道转发到WebSocket接收器时,类型不匹配解决错误类型

时间:2017-11-30 21:03:37

标签: rust future rust-tokio

我正试图在Rust中围绕期货,但我对此代码感到困惑,该代码应该将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"); }

stream

如评论中所述,使用rx作为输入可以正常工作。使用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` 时,编译器会抱怨错误类型的类型不匹配(我相信):

[dependencies]
websocket = "0.20.0"
futures = "0.1"
tokio-core = "0.1"

这些是我的依赖项:

{{1}}

我在这里缺少什么?

1 个答案:

答案 0 :(得分:7)

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 == ()`

我们在这里有两种类型:<futures::stream::SplitSink<...> as futures::Sink>::SinkError()。这两种类型来自哪里?另外,第一个是未解决的关联类型;也许我们可以解决它以获得更多的洞察力?让我们一步一步地追踪它。

首先,我们需要弄清楚编译器首先尝试匹配这两种类型的原因。如果我们查看forward的签名,我们会看到约束Self::Error: From<S::SinkError>Self是我们正在调用forward的流的类型,而S是将forward作为参数传递给forward的接收器类型}。

我们在rx上呼叫futures::sync::mpsc::Receiver,其类型为impl<T> Stream for Receiver<T> type Item = T type Error = () 。在documentation page for Receiver上,我们可以看到以下内容:

()

这向我们展示了sink来自哪里。现在让我们看看sink参数。

futures::stream::SplitSink<websocket::client::async::Framed<tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>>的类型是impl<S: Sink> Sink for SplitSink<S> type SinkItem = S::SinkItem type SinkError = S::SinkError (我们从错误消息中知道这一点; RLS也确认了这一点)。在documentation page for SplitSink上,我们有:

SplitSink

因此SinkError&#39; SinkError与其内部websocket::client::async::Framed<tokio_core::net::TcpStream, websocket::async::MessageCodec<websocket::OwnedMessage>>相同。内部接收器的类型是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 documentation for Framed说什么?

Framed

websocket::async::MessageCodec<websocket::OwnedMessage>有两个类型参数,但我们只需要查看第二个参数,即SinkError,以确定websocket::codec::ws::MessageCodec类型。现在让我们来看看MessageCodec。 (注意:websocket::async::MessageCodec reexportedimpl<M> Decoder for MessageCodec<M> where M: MessageTrait, type Item = OwnedMessage type Error = WebSocketError

Self::Error: From<S::SinkError>
啊哈哈!接收器产生WebSocketError类型的错误。

现在我们已经确定了这些类型,让我们回过头来讨论这些类型的原因。我们试图理解为什么在调用forward时没有遇到约束(): From<WebSocketError>。我们现在知道编译器正在尝试解析impl From<WebSocketError> for ()。看起来似乎没有extern crate websocket; fn main() { let a = websocket::result::WebSocketError::NoDataAvailable; let () = From::from(a); } 。我们来验证一下:

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`

确实,这无法编译:

sink

我们可以使用sink_map_err更改let (sink, stream) = s.split(); let sink = sink.sink_map_err(|_| ()); // <<<<< rx .forward(sink) .and_then(|(a, sink)| { sink.send(OwnedMessage::Close(None)) }) 的错误类型来解决缺失的实施问题。

forward

这解决了对upgrade.use_protocol("rust-websocket").accept()的调用,但现在此闭包的结果并未与WebSocketError组成,rx仍然有WebSocketError作为其错误类型。更改()的错误类型更有意义。但是我们如何从Receiver构建一个(),它没有信息?

您可能想知道,为什么poll使用!作为错误类型?如果我们查看source code,我们可以看到,实际上,WebSocketError 从不会返回错误。我认为如果错误类型是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"); } (从不类型)或其他一些void类型更合适,以清楚地表明错误是不可能的;有一个issue open on futures要求对期货0.2进行此更改。

由于错误是不可能的,我们不需要构建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 ;我们可以改为分散,例如恐慌。

rx

现在,还有一个错误:

forward

为什么关闭试图移动self?因为FnMut按值FnOnce获取。为什么闭包是FnOnce?请注意,Future::and_then需要FnMut(它有效将值从捕获的变量移动到for_each闭包),但Stream::for_each需要{multi-producer, single-consumer 1}}。这是有道理的:{{1}}将为每个传入连接调用一次闭包!

您正在使用的频道为{{3}}(因此名称为 mpsc ),但您尝试在此处拥有多个消费者(每个连接都在尝试阅读来自接收者)。我将留给您修复程序中的此设计问题。请记住,可以有多个并发客户端连接!