Skip to content

Async closure argument inference is broken with functions taking FnOnce() -> futures::TryFuture #127781

Closed

Description

@peku33

I'm writing this as a continuation of #127425

While the problem from first message in issue seems to be fixed, there are still cases when this fails.

One of examples is when using try_for_each instead of for_each, when closure is expected to return Result:

#![feature(async_closure)]

use anyhow::Error;
use futures::{stream::repeat_with, StreamExt, TryStreamExt};

fn accept_str(_: &str) {}
fn accept_string(_: &String) {}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Error> {

    repeat_with(|| "foo".to_owned())
        .take(1)
        .map(Result::<_, Error>::Ok)
        .try_for_each(async move |value| {
            // error on whole closure, rust thinks that type of value is `str`
            accept_str(&value);

            // type annotations needed, cannot infer type
            accept_str(value.as_str());

            // this works
            accept_string(&value); // ok
            
            Ok(())
        })
        .await?;

    Ok(())
}

I expected to see this happen: All options used to work in past versions of rust

Instead, this happened: Without explicit type hint it fails

Version it worked on

It most recently worked on: Not 100% sure, some explanations have been made in original issue

Version with regression

rustc 1.81.0-nightly (d9284afea 2024-07-14)
binary: rustc
commit-hash: d9284afea99e0969a0e692b9e9fd61ea4ba21366
commit-date: 2024-07-14
host: x86_64-pc-windows-msvc
release: 1.81.0-nightly
LLVM version: 18.1.7

Activity

  1. added
    C-bugCategory: This is a bug.
    regression-untriagedUntriaged performance or correctness regression.
    on Jul 15, 2024
  2. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 15, 2024
  3. added
    A-closuresArea: Closures (`|…| { … }`)
    WG-asyncWorking group: Async & await
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    regression-untriagedUntriaged performance or correctness regression.
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 15, 2024
  4. compiler-errors commented on Jul 15, 2024

    @compiler-errors
    Contributor

    This is because the signature of try_for_each is:

    fn try_for_each<Fut, F>(self, f: F) -> TryForEach<Self, Fut, F>
    where
        F: FnMut(Self::Ok) -> Fut,
        Fut: TryFuture<Ok = (), Error = Self::Error>,
        Self: Sized,
    

    We only extract an async closure bound from cases where there is Future<Output = ???> bound on the trait, which TryFuture is not. There isn't even a supertrait bound that constrains the output type.

    I also do not expect this to ever be fixed, because that would require overly complicating the closure signature inference algorithm in ways that make me quite uncomfortable12. The correct solution is to rework the futures crate to have first-class async closure support, so for now I'd recommend just ascribing your type names. Sorry!

    Footnotes

    1. Firstly, this would break if another blanket implementation for TryFuture were ever added. ↩

    2. Secondly, the combinator shouldn't really be using FnMut bounds either -- it should be using async FnMut bounds. ↩

  5. compiler-errors commented on Jul 15, 2024

    @compiler-errors
    Contributor

    Alternatively, rust-lang/futures-rs#2763 would fix this when paired with a slight adjustment to the closure signature inference that I'd be happy to make, but it seems like that PR is blocked.

  6. changed the title [-]Async closure argument inference is broken with closures returning Result[/-] [+]Async closure argument inference is broken with functions taking `futures::TryFuture`[/+] on Jul 16, 2024
  7. 10 remaining items

  8. added a commit that references this issue on Dec 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-closures`async || {}`A-closuresArea: Closures (`|…| { … }`)A-inferenceArea: Type inferenceC-bugCategory: This is a bug.T-typesRelevant to the types team, which will review and decide on the PR/issue.WG-asyncWorking group: Async & await

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions