Repository: rust
Issue: Delay stabilizing async closures to consider if they should return `impl IntoFuture` instead of `impl Future`
Labels: T-lang,C-discussion,I-lang-nominated

Async closures are currently in beta for stabilization in 1.85. I'm very excited to see this feature stabilized!

That said, I would like the Lang team to consider delaying their stabilization to consider a new design detail. Delaying stabilization would amount to putting a PR in nightly that undoes the stabilization, and then doing a beta backport.

Note that this doesn't necessarily mean the lang team would be committing to making this change, but if the lang team feels there's a non-trivial chance they would prefer the `IntoFuture` semantics, then we should delay stabilization so there is time to consider this.

I talked with @tmandry and @traviscross about this and they did not remember discussing this particular detail during the stabilization process. If we are mistaken and this was discussed, then we should let the decision stand and not change anything.

# Why should T-lang consider this change?

@yoshuawuyts recently published a [blog post about generators and auto trait leakage](https://blog.yoshuawuyts.com/gen-auto-trait-problem/), which argues that `gen {}` should return `impl IntoIterator` instead of `impl Iterator`. The key example in the post is (adapted slightly):

```rust
let iter = gen {
    let rc = Rc::new(...);
    yield 12u32;
    rc.do_something();
};

spawn(|| {
    for num in iter {
        println!("{num}");
    }
}).unwrap();
```

If `gen {}` returns `impl Iterator`, as it does on nightly today, this example fails to compile because the `Rc` is not `Send`. However, this is overly restrictive because the `Rc` is not created until the spawned thread calls `next()` the first time.

Instead, having `gen {}` return `impl IntoIterator` allows this to work, because the `IntoIterator` part could be `Send`, and once it becomes an `Iterator` it would no longer be `Send`.

This has a similar shape to annoyances we've seen with async Rust, where it's not uncommon for people to want to write futures that are `Send` until they are polled for the first time. If we had made `async {}` blocks return `impl IntoFuture` instead, this would be straightforward to do for the same reasons.

So then the next logical question is what should generator closures or async closures do? For much the same reason, it would be nice for async closures to return futures that are `Send` until first polled.

At this point, changing the return type for `async {}` would require an edition change. However, we have a brief window where we can still make this change for async closures.

# Why should T-lang keep the status quo?

A lot of care has gone into the current design by @compiler-errors and others to come up with something that balances a lot of competing goals. For example, with the current design, the following example works, but it would break with this proposed change:

```rust
fn foo<F, U>(_f: F)
where
    F: Fn() -> U,
    U: Future
{}

// can be invoked with legacy, fake async closures or new, real async closures
foo(|| async {});
foo(async || {});
```

This is a desirable property, since a lot of existing ecosystem code is written using `Fn() -> impl Future` as an approximation of an async closure. If async closures returned `IntoFuture` then they would not work until existing code is updated to support async closures natively.

I bring this up mainly to show that it's not obvious that we actually should make this change. There are many factors in tension and there's a good chance we already have the optimal balance. We'd want to thoroughly consider any changes, so this issue is mostly about whether we want to take the time to consider those changes.

# Mitigating Factors

This would still be changeable over an edition if we were to decide it's worthwhile but don't want to churn things now. If we were to make this change, we'd likely want to change the behavior of `async {}`. Doing both over an edition at the same time would keep the overall language more consistent.