Search code examples
rustreqwest

the `?` operator can only be used in an async block that returns `Result` or `Option


I'm learning rust and trying to scrape a random site by sending some POST data, and I'm getting a bunch of errors like:

error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)

37  | |     let text = res.text().await?;
    | |                                ^ cannot use the `?` operator in an async block that returns `()`

The code is below. I've gone through the docs multiple times, but I'm returning a Result<> so I'm not sure what's wrong.

use std::sync::Arc;


#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Hello, world!");

    use reqwest::{cookie::Jar, Url};
    use reqwest::header;

    let mut headers = header::HeaderMap::new();
    headers.insert("User-Agent", header::HeaderValue::from_static("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"));
    headers.insert("Content-Type", header::HeaderValue::from_static("application/json"));
    
    let cookie = "cookies";

    let jar = Jar::default();
    let cookie_url = "https://url.com/".parse::<Url>()?;
    jar.add_cookie_str(cookie, &cookie_url);

    let body = String::from("post body"); 

    let client = reqwest::Client::builder()
    .default_headers(headers)
    .cookie_store(true)
    .cookie_provider(Arc::new(jar))
    .build()?;

    let path = "https://long_url.com".parse::<Url>()?;

    let res = client
        .post(path)
        .body(body)
        .send()
        .await?;

    let text = res.text().await?;
    println!("{}", text);

    Ok(());
}

Thank you!


Solution

  • The message is a bit confusing because of the transformations being performed, you may want to report this on the bug tracker, but do note the wording:

    ^ cannot use the ? operator in an async block that returns ()

    It uses the word "block", not function. Because this is really a cascading error: behind the scenes, an async function is converted into a state machines where each await is a "yield point" between blocks of code, so

    let res = client
            .post(path)
            .body(body)
            .send()
            .await?;
    
        let text = res.text().await?;
        println!("{}", text);
    
        Ok(());
    

    can be thought of along the lines of (this is not the actual transformation, if you want an actual deconstruction Jon Gjengset has an extensive video on the subject)

    let future = async {
        client.post(path).body(body).send()
    };
    yield future;
    let future = async {
        let res = future.value()?;
        res.text()
    };
    yield future;
    let future = async {
        let text = future.value()?;
        println!("{}", text);
    
        Ok(());
    };
    return future;
    

    Note the last expression of the last block, this is where the problem occurs: it's Ok(());.

    The trailing semicolon will "suppress" the expression's normal value and result in a (), not Result<...>, hence a type incompatibility and the error you see: the return value of the block is incoherent.

    At the end of the pile of async block errors, the compiler actually shows the "source" error:

    error[E0308]: mismatched types
     --> src/main.rs:6:20
      |
    6 | async fn main() -> Result<(), Box<dyn std::error::Error>> {
      |          ----      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Result`, found `()`
      |          |
      |          implicitly returns `()` as its body has no tail or `return` expression
      |
      = note:   expected enum `Result<(), Box<(dyn std::error::Error + 'static)>>`
              found unit type `()`
    

    and if you look at the entire "async block" error, it takes care to show the errorneous Ok(()); every time:

    error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)
       --> src/lib.rs:19:55
        |
    6   |   async fn main() -> Result<(), Box<dyn std::error::Error>> {
        |  ___________________________________________________________-
    7   | |     println!("Hello, world!");
    8   | |
    9   | |     use reqwest::{cookie::Jar, Url};
    ...   |
    19  | |     let cookie_url = "https://url.com/".parse::<Url>()?;
        | |                                                       ^ cannot use the `?` operator in an async block that returns `()`
    ...   |
    41  | |     Ok(());
    42  | | }
        | |_- this function should return `Result` or `Option` to accept `?`
        |
        = help: the trait `FromResidual<Result<Infallible, url::parser::ParseError>>` is not implemented for `()`
    note: required by `from_residual`