rustrust-tokiohyperx-www-form-urlencoded

Cannot use `form_urlencoded::Serializer` in `async` function


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?


Solution

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