rust-lang / rust-lang/rust-clippy

manual_async_fn suggestions breaks code if !Send types involved

Open
#12,664 0 comments 6 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug I-suggestion-causes-error
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

In suggesting a fn …(…) -> impl Future<Output=T> + Sendasync fn …(…) -> T transformation, clippy can break code where !Send types are involved.

Reproducer

I tried this code:

use std::future::Future;
use std::rc::Rc;

trait Request {
    fn get_body(self) -> impl Future<Output=Vec<u8>> + Send;
}

struct MyRequest {
    _thing: Rc<u8>,
    body: Vec<u8>,
}

impl Request for MyRequest {
    fn get_body(self) -> impl Future<Output=Vec<u8>> + Send {
        async { self.body }
    }
    
    // Clippy's suggestion, which does not compile.
    /*
    async fn get_body(self) -> Vec<u8> {
        self.body
    }
    */
}

fn main() {
}

(Playground)

On this, Clippy emits:

warning: this function can be simplified using the `async fn` syntax
  --> src/main.rs:14:5
   |
14 |     fn get_body(self) -> impl Future<Output=Vec<u8>> + Send {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
   = note: `#[warn(clippy::manual_async_fn)]` on by default
help: make the function `async` and return the output of the future directly
   |
14 |     async fn get_body(self) -> Vec<u8> {
   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: move the body of the async block to the enclosing function
   |
14 |     fn get_body(self) -> impl Future<Output=Vec<u8>> + Send { self.body }
   |                                                             ~~~~~~~~~~~~~

Which seems reasonable ish (although the second help feels like it ought to have an async fn signature, given the transformation to the body requires it…). If we trade the commented section (what I believe Clippy's suggestion to be), however, that breaks things:

error: future cannot be sent between threads safely
  --> src/main.rs:21:5
   |
21 |     async fn get_body(self) -> Vec<u8> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `get_body` is not `Send`
   |
   = help: within `impl Future<Output = Vec<u8>>`, the trait `Send` is not implemented for `Rc<u8>`, which is required by `impl Future<Output = Vec<u8>>: Send`
note: captured value is not `Send`
  --> src/main.rs:21:23
   |
21 |     async fn get_body(self) -> Vec<u8> {
   |                       ^^^^ has type `MyRequest` which is not `Send`
note: required by a bound in `Request::{opaque#0}`
  --> src/main.rs:5:56
   |
5  |     fn get_body(self) -> impl Future<Output=Vec<u8>> + Send;
   |                                                        ^^^^ required by this bound in `Request::{opaque#0}`

(In this minimal example, I've made the MyRequest type !Send by involving a Rc. The real-world use case that got tripped up here involved an actix_web::HttpRequest, which is also !Send, again by way of holding onto an Rc.)

Version

On the playground:

Rust 1.77.2 (stable)
Clippy 0.1.77

Locally:

» rustc -vV
rustc 1.77.1 (7cf61ebde 2024-03-27)
binary: rustc
commit-hash: 7cf61ebde7b22796c69757901dd346d0fe70bd97
commit-date: 2024-03-27
host: x86_64-unknown-linux-gnu
release: 1.77.1
LLVM version: 17.0.6
Additional Labels

@rustbot label +I-suggestion-causes-error

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided Rust reproducer and tracing the manual_async_fn lint's suggestion generation. Check how the transformation handles Send bounds when the function receiver contains a !Send type such as Rc. Done means the lint no longer suggests code that fails to compile for this case, while valid transformations remain supported.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.