Why compiler says that argument doesn't implement required traits?

477 Views Asked by At

I started learning Rust language for a while ago and now I am working on implementing a websocket server for a personal project. With this said, I am not a professional Rust programmer and I am still in the phase of learning the basics.

I develop this project with three libraries, tokio, hyper and tokio-tungstenite. My HTTP server is written in hyper. There is basically only one handler. This handler does one thing, upgrades the incoming UPGRADE HTTP requests to WebSocket connections.

use std::str;

use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::prelude::*;

use hyper::header::{HeaderValue, UPGRADE, SEC_WEBSOCKET_ACCEPT};
use hyper::service::{make_service_fn, service_fn};
use hyper::upgrade::Upgraded;
use hyper::{Body, Client, Request, Response, Server, StatusCode};
use std::net::SocketAddr;

// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

async fn server_upgrade(req: Request<Body>) -> Result<Response<Body>> {
    let mut res = Response::new(Body::empty());

    // Send a 400 to any request that doesn't have
    // an `Upgrade` header.
    if !req.headers().contains_key(UPGRADE) {
        *res.status_mut() = StatusCode::BAD_REQUEST;
        return Ok(res);
    }

    // Setup a future that will eventually receive the upgraded
    // connection and talk a new protocol, and spawn the future
    // into the runtime.
    //
    // Note: This can't possibly be fulfilled until the 101 response
    // is returned below, so it's better to spawn this future instead
    // waiting for it to complete to then return a response.
    let upgraded: Upgraded = match req.into_body().on_upgrade().await {
      Ok(upgraded) => upgraded,
      Err(e) => {
        eprintln!("upgrade error: {}", e);
        return Err(Box::new(e));
      },
    };
    tokio_tungstenite::accept_async(upgraded);
    Ok(res)
}

In the last 3rd line, the compiler emits an error saying these:

the trait bound `hyper::upgrade::Upgraded: tokio::io::async_read::AsyncRead` is not satisfied
the trait `tokio::io::async_read::AsyncRead` is not implemented for `hyper::upgrade::Upgraded`

the trait bound `hyper::upgrade::Upgraded: tokio::io::async_write::AsyncWrite` is not satisfied
the trait `tokio::io::async_write::AsyncWrite` is not implemented for `hyper::upgrade::Upgraded`

But when I look at the documentation for hyper::upgrade::Upgraded, I can see that it actually implements those traits. As I also look at the tokio-tunsgstenite's documentation, indeed these traits are the same traits from the same crate.

Could an experience rustecean help me how to resolve this issue and convince the compiler that the upgraded instance implements the same traits? Maybe a version mismatch among crates?

I am also attaching my Cargo.toml file here.

[package]
name = "async-rs"
version = "0.1.0"
authors = ["Bora Semiz <Bora Semiz's email address>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hyper = "0.13.9"
tokio = { version = "^0.2", features = ["full"] }
tokio-tungstenite = "0.12.0"
1

There are 1 best solutions below

0
On BEST ANSWER

Hyper is using Tokio version 0.2.x, while tokio-tungstenite is using Tokio version 0.3.x. The changelog from Tokio 0.2 to 0.3 included breaking changes to the AsyncRead and AsyncWrite traits, making the two incompatible with their version 0.2 counterparts.

You can use the tokio_compat_02 crate to bridge the two until Hyper release a new version (also upgrade your own tokio version to 0.3 in Cargo.toml), alternatively downgrade tokio-tungstenite to 0.11.0 which uses tokio 0.2.x if you prefer.