-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mutually recursive async fn
s are hard to make Send
#62284
Comments
So this is tied to cycles amongst impl Trait. The problem arises because we need to know the hidden type of #![feature(async_await)]
use {
std::{
future::Future,
pin::Pin,
},
};
type BoxFuture = Pin<Box<dyn Future<Output = ()> + Send>>; // adding Send causes a cycle error
fn foo() -> impl Future<Output = BoxFuture> + Send {
async {
Box::pin(bar()) as BoxFuture
}
}
async fn bar() {
let _ = foo().await;
}
fn main() { } |
It's tricky but not impossible to imagine a more flexible handling of auto traits and leakage. We've discussed it before and it's probably a good idea. But I think it's pretty orthogonal from async-await. Therefore, presuming that we want to keep our current desugaring, I think that we should probably consider this as non-blocking -- but it's definitely something to add to the list of "surprises that may trip up regular users". |
It's actually sort of mysterious to me that this example builds at all. For example, in trying to convert this recursive async fn to something that builds: #![feature(async_await)]
async fn foo(n: usize) {
if n > 0 {
Box::new(foo(n - 1)).await;
}
}
fn is_send<T: Send>(t: T) { drop(t); }
fn main() {
is_send(foo());
} The only thing I could find which works is to use a #![feature(async_await)]
use std::future::Future;
use std::pin::Pin;
fn foo(n: usize) -> Pin<Box<dyn Future<Output = ()> + Send>> {
Box::pin(async move {
if n > 0 {
foo(n - 1).await;
}
})
}
fn is_send<T: Send>(t: T) { drop(t); }
fn main() {
is_send(foo(22));
} Any other setup (including, say, this one that uses This is a consequence, as far as I can tell, of our desugaring to generators, which in turn is currently modeled after our approach to closures, and closures cannot capture themselves. This is needed to make "upvar inference" manageable. It occurs to me, though, that this restriction is not needed for |
I guess that the reason the original example sidesteps these problems is because |
@nikomatsakis Right-- the point here is that there shouldn't be a cycle at all, because the value that appears inside the generator is not the future type itself, but the type |
Discussed in meeting today. Filed #62539 to try and improve diagnostics for the naive case. I am marking as "unclear" because it might be good enough for stabilization purposes to document the correct workarounds (rust-lang/async-book#22) and improve error messages (#62539). |
There are several other related issues to this, but I'm opening this to track this one specifically since it's a pain-- there are workarounds, but it'd be lovely (and should be possible) to make this "just work." The following example compiles just fine without
+ Send
, but adding theSend
bound causes a cycle error:Working around the cycle error is possible, but annoying:
Ideally we wouldn't have a cycle error in either case, since it is possible to see that
foo
must beSend
without ever looking at the body ofbar
, sincebar
is immediately boxed into aBoxFuture
.The text was updated successfully, but these errors were encountered: