Info
This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Borrow of owned !Sync type in async function
The following program fails to compile because the future returned by asdf
is not Send. b
(&Cell
) is not Send because Cell
is not Sync, and this borrow is held across an await point, therefore the future is not Send.
use std::cell::Cell;
fn must_send(_: impl Send) {}
async fn asdf() {
let a = Cell::new(1);
let b = &a;
async {}.await;
// let b = &a;
println!("{}", b.get());
}
fn main() {
let _ = must_send(asdf());
}
Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=0587e412c0ff548b37a8e26976bcd2ce
This makes sense to me if the async function doesn't own the Cell. For example, if you had an async function that accepted a &T
where T: !Sync
, you cannot poll the future on another thread since that would allow you to access T
on multiple threads simultaneously.
However, in this case, the async function has ownership over T
. Even if you were to poll the future on another thread, T
and &T
would "move" together, which I believe means that it would not be possible to simultaneously access T
from multiple threads. Therefore, even though &T
is not Send, you would still be able to safely hold &T
across an await point in a Send future, assuming that the async function owns T
.
Is my reasoning correct here? Is there a case where it would not be safe to hold a &T
borrow (where T: !Sync
) across an await point in a Send future while having ownership of T
? If this is in fact safe, is this just one of those cases where it is not worth it for the compiler to incur more complexity to handle such things?
I encountered this with something like the following code:
#[axum::debug_handler]
async fn handle_something(headers: HeaderMap, request: Request) -> Response {
let the_response = || request.uri().to_string().into_response();
// closure the_response borrows `request`, which is not Sync, across await point
// can fix this by doing `let uri = request.uri()` then borrowing `uri` in closure
if !something(&headers) {
return the_response();
}
if !async_something(&headers).await { // <-- await point here
the_response() // <-- closure used here
} else {
"ok".into_response()
}
}
3 posts - 2 participants
🏷️ Rust_feed