0

I have a hyper server set up more or less exactly as the third example here: https://docs.rs/hyper/0.14.16/hyper/server/index.html . My version of the handle function calls some other async functions, and everything works fine, until I try to encode some URL query params into a string in one of those async functions. My project stops compiling when I include the four lines involving the Serializer in one of the functions called by handle:

async fn broken_func(&self, param: &str) -> Result<String, Infallible> {
    // ...
    
    let mut s = url::form_urlencoded::Serializer::new(String::new());
    
    // the presence or absence of these two lines has no effect on the bug, but
    // they demonstrate how I'm trying to use the Serializer
    s.append_pair("key", "value");
    println!("{:?}", s.finish());
    
    drop(s); // <-- thought this might help, but it doesn't
    
    // ...
    Ok(query_string)
}

The error I get is

generator cannot be sent between threads safely
the trait `std::marker::Sync` is not implemented for `dyn for<'r> std::ops::Fn(&'r str) -> std::borrow::Cow<'_, [u8]>`

I have no idea what this has to do with form_urlencoded::Serializer. However, I am aware that Serializer is both !Send and !Sync, but in this case I'm only using it within a single function so I don't think that should make a difference? If I remove those four lines above, it goes back to compiling.

So instead, to serialize some key/value pairs into URL query parameters, I have to use the following, which kind of seems ridiculous -- not just because this is needlessly complex for something so simple, but also because url::Url::parse_with_params uses form_urlencoded::Serializer under the hood.

let query_string = url::Url::parse_with_params(
        "http://example.com", 
        &[("key", "value")]
    )
    .unwrap()
    .query()
    .map(|s| s.to_owned())
    .unwrap();

Any idea why trying to explicitly use Serializer inside an async function causes things to break?

BallpointBen
  • 9,406
  • 1
  • 32
  • 62
  • 1
    Can you provide a full example? I can't reproduce your issue. Pulling in `url` and passing & awaiting the `broken_func()` future works with this function: `async fn consume_sync>>(s: S)` – sebpuetz Jan 18 '22 at 08:58
  • 1
    One thing you could try is: put your `s` into a scope, and make sure there's no `await` in that scope. Maybe there's something else leaking into the future, and you've missed it? (I've also tried and cannot reproduce this.) – Caesar Jan 18 '22 at 13:09

1 Answers1

-1

Caesar hit the nail on the head. The trick of putting s in a scope fixed it.

BallpointBen
  • 9,406
  • 1
  • 32
  • 62