Tide: A problem with switching from 0.3 to 0.4

Created on 17 Dec 2019  路  9Comments  路  Source: http-rs/tide

I use mobc as my connection pool.
Here is the HelloWorld demo.

pub struct Global {
    redis: Pool<RedisConnectionManager<DefaultExecutor>>,
    postgres: Pool<PostgresConnectionManager<NoTls, DefaultExecutor>>,
}

async fn helloworld(cx: Request<Global>) -> String { 
    match cx.state().redis.get().await {
        Ok(_) => "Rust rua!!!".to_string(),
        Err(_e) => "SERVER ERROR".to_string(),
    }
}

It's ok in tide 0.3, but it complied failed in tide 0.4

Here is the error message, my pool is sync + send + 'static, I don't know which part is unsync

error[E0277]: `(dyn futures_io::if_std::AsyncBufRead + std::marker::Send + 'static)` cannot be shared between threads safely
  --> api/src/main.rs:84:17
   |
84 |     app.at("/").get(helloworld);
   |                 ^^^ `(dyn futures_io::if_std::AsyncBufRead + std::marker::Send + 'static)` cannot be shared between threads safely
   |
   = help: the trait `std::marker::Sync` is not implemented for `(dyn futures_io::if_std::AsyncBufRead + std::marker::Send + 'static)`
   = note: required because of the requirements on the impl of `std::marker::Sync` for `std::ptr::Unique<(dyn futures_io::if_std::AsyncBufRead + std::marker::Send + 'static)>`
   = note: required because it appears within the type `std::boxed::Box<(dyn futures_io::if_std::AsyncBufRead + std::marker::Send + 'static)>`
   = note: required because it appears within the type `std::pin::Pin<std::boxed::Box<(dyn futures_io::if_std::AsyncBufRead + std::marker::Send + 'static)>>`
   = note: required because it appears within the type `http_service::Body`
   = note: required because it appears within the type `http::request::Request<http_service::Body>`
   = note: required because it appears within the type `tide::request::Request<Global>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `&tide::request::Request<Global>`
   = note: required because it appears within the type `for<'r, 's, 't0, 't1> {tide::request::Request<Global>, &'r tide::request::Request<Global>, Global, &'s Global, &'t0 mobc::Pool<mobc_postgres::PostgresConnectionManager<tokio_postgres::tls::NoTls, mobc::runtime::DefaultExecutor>>, mobc::Pool<mobc_postgres::PostgresConnectionManager<tokio_postgres::tls::NoTls, mobc::runtime::DefaultExecutor>>, impl std::future::Future, ()}`
   = note: required because it appears within the type `[static generator@api/src/main.rs:34:52: 43:2 cx:tide::request::Request<Global> for<'r, 's, 't0, 't1> {tide::request::Request<Global>, &'r tide::request::Request<Global>, Global, &'s Global, &'t0 mobc::Pool<mobc_postgres::PostgresConnectionManager<tokio_postgres::tls::NoTls, mobc::runtime::DefaultExecutor>>, mobc::Pool<mobc_postgres::PostgresConnectionManager<tokio_postgres::tls::NoTls, mobc::runtime::DefaultExecutor>>, impl std::future::Future, ()}]`
   = note: required because it appears within the type `std::future::GenFuture<[static generator@api/src/main.rs:34:52: 43:2 cx:tide::request::Request<Global> for<'r, 's, 't0, 't1> {tide::request::Request<Global>, &'r tide::request::Request<Global>, Global, &'s Global, &'t0 mobc::Pool<mobc_postgres::PostgresConnectionManager<tokio_postgres::tls::NoTls, mobc::runtime::DefaultExecutor>>, mobc::Pool<mobc_postgres::PostgresConnectionManager<tokio_postgres::tls::NoTls, mobc::runtime::DefaultExecutor>>, impl std::future::Future, ()}]>`
   = note: required because it appears within the type `impl std::future::Future`
   = note: required because it appears within the type `impl std::future::Future`
   = note: required because of the requirements on the impl of `tide::endpoint::Endpoint<Global>` for `fn(tide::request::Request<Global>) -> impl std::future::Future {helloworld}`

Most helpful comment

req.pool needed to be assigned to a temporary, which fixed the trait resolver problems.

@yoshuawuyts I know you said the ping wasn't a bug report, but I see that even though that change will be obvious to anyone reading the output with a moderate level of experience with Rust, that hint is _not_ in the output and can be beyond the level of understanding of someone just starting out (or a decaffeinated experienced dev). That makes me think we might want to add a help: consider assigning this to a temporary variable message at the bottom in rustc for these cases. What do you think? If you agree, would you mind opening a ticket for it?

All 9 comments

@importcjj o/ thanks for reporting! At a glance I'm unsure what's causing this failure. Just to verify: which Rust version are you using? Did you only update the Tide version, or also Rust versions?

I just wrote a quick example to verify that we can indeed read from the global state:

use async_std::io;
use async_std::task;

/// Shared application state.
#[derive(Debug)]
struct State {
    name: String,
}

fn main() -> io::Result<()> {
    task::block_on(async {
        let mut app = tide::with_state(State { name: "nori".to_string() });
        app.at("/submit").post(|req: tide::Request<State>| {
            async move {
                let name = &req.state().name;
                tide::Response::new(200).body_string(name.to_string())
            }
        });
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    })
}

I'd be happy to help you debug further if you could share a repro. I'm sorry this is happening, but thanks again for reporting!

@yoshuawuyts

My Rust version

stable-x86_64-unknown-linux-gnu (default)
rustc 1.39.0 (4560ea788 2019-11-04)

Here is a simple demo.

https://github.com/importcjj/tide-demo/blob/master/src/main.rs

@importcjj I updated to a recent nightly version, and the diagnostics were super helpful! Solved the problem, and included the code. The short version is: req.pool needed to be assigned to a temporary, which fixed the trait resolver problems.

I'm not exactly sure what caused this to trip up in a recent Tide version, but the problem is easily sidestepped. It seems somewhat similar to the lifetime inference changes that happened as part of 1.39-nightly (https://github.com/rust-lang/rust/pull/64292), but you mentioned you'd been using 1.39-stable all along so I really don't know.

Happy this seems to have been resolved though. Let me know how this works out for you, and then I think we should be okay to close this. Thanks!

Screenshot

2019-12-17-175740_1920x1080

Code

use async_std::io;
use async_std::task;

use mobc::{ConnectionManager, runtime::DefaultExecutor, Pool, AnyFuture};

struct FooManager;

struct FooConnection;

impl FooConnection {
    async fn query(&self) -> String {
        "nori".to_string()
    }
}

impl ConnectionManager for FooManager {
    type Connection = FooConnection;
    type Error = std::io::Error;
    type Executor = DefaultExecutor;

    fn get_executor(&self) -> Self::Executor {
        DefaultExecutor::current()
    }

    fn connect(&self) -> AnyFuture<Self::Connection, Self::Error> {
        Box::pin(futures::future::ok(FooConnection))
    }

    fn is_valid(&self, conn: Self::Connection) -> AnyFuture<Self::Connection, Self::Error> {
        Box::pin(futures::future::ok(conn))
    }

    fn has_broken(&self, conn: &mut Option<Self::Connection>) -> bool {
        false
    }
}

/// Shared application state.
struct State {
    pool: Pool<FooManager>
}

fn main() -> io::Result<()> {
    task::block_on(async {
        let pool = Pool::new(FooManager).await.unwrap();
        let mut app = tide::with_state(State { pool });
        app.at("/submit").post(|req: tide::Request<State>| {
            async move {
                let pool = &req.state().pool;
                let conn = pool.get().await.unwrap();
                let name = conn.query().await;
                tide::Response::new(200).body_string("hello".to_string())
            }
        });
        app.listen("127.0.0.1:8080").await?;
        Ok(())
    })
}

req.pool needed to be assigned to a temporary, which fixed the trait resolver problems.

@yoshuawuyts I know you said the ping wasn't a bug report, but I see that even though that change will be obvious to anyone reading the output with a moderate level of experience with Rust, that hint is _not_ in the output and can be beyond the level of understanding of someone just starting out (or a decaffeinated experienced dev). That makes me think we might want to add a help: consider assigning this to a temporary variable message at the bottom in rustc for these cases. What do you think? If you agree, would you mind opening a ticket for it?

@estebank done!

Thank you!
But this error message really confused me.

@importcjj is the new error understandable to you? If not, please join the thread at the linked rust ticket.

@estebank The new error message is clear enough, but I'm just confused about the tide state() API.

@yoshuawuyts Why not just use req.state.pool instead of req.state().pool? After all, they do the same thing, and the former doesn't cause errors.

@importcjj providing public field access, unless directly modifiable as part of a constructor, is somewhat of an anti-pattern. Not too thrilled about type inference having a bad time here, but my hopes is that as work on async/await continues, inference will eventually catch up.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eignnx picture eignnx  路  4Comments

realcr picture realcr  路  4Comments

cquintana-verbio picture cquintana-verbio  路  6Comments

milesgranger picture milesgranger  路  6Comments

gruberb picture gruberb  路  4Comments