static-assertions
static-assertions copied to clipboard
Attribute macros
There is a problem I kept running into with async functions with something like Axum/Hyper.
With the async function syntax, it is very easy for its output Future to accidentally not be Send because the compiler thinks you are holding onto something that is not Send across an await point. One example would be a (synchronous/blocking) MutexGuard.
For example:
pub struct Foo<T: Send + Clone + Default> {
shared: Arc<Mutex<T>>,
}
impl<T: Send + Clone + Default> Foo<T> {
pub async fn this_is_correctly_send(&self) -> T {
let thing = self.shared.lock().unwrap().clone();
sleep(Duration::from_secs(1)).await;
thing
}
pub async fn this_is_also_send(&self) -> T {
let thing = {
let mut guard = self.shared.lock().unwrap();
mem::replace(guard.deref_mut(), T::default())
};
sleep(Duration::from_secs(1)).await;
thing
}
pub async fn this_is_accidentally_not_send(&self) -> T {
let mut guard = self.shared.lock().unwrap();
let thing = mem::replace(guard.deref_mut(), T::default());
// As of now, whether this drop is here makes no difference to the compiler
// Eventually, -Zdrop-tracking will change that
drop(guard);
sleep(Duration::from_secs(1)).await;
thing
}
}
Some related background information:
- https://rust-lang.github.io/wg-async/vision/submitted_stories/status_quo/alan_thinks_he_needs_async_locks.html
- https://github.com/rust-lang/rust/issues/57478
- https://github.com/rust-lang/rust/issues/97331
This matters because the !Send-ness can propagate up very far and eventually when mounting the handler to something like the Axum router, it causes a cryptic error and takes a long time to trace back to where the problem is.
To guard against these kind of issues, I can do something like this (a bit simplified):
impl Foo {
async fn foo() {
// ...
}
#[allow(dead_code)]
fn __foo_must_be_send(&self) {
#![allow(unused_must_use)]
fn it_sends<T: Send>(it: T) -> T {
it
}
it_sends(self.foo());
}
}
This works but is pretty distracting. It would be nice to be able to do something like:
impl Foo {
#[assert_impl(Send)]
async fn foo() {
// ...
}
}
While looking for something that does that, this crate came up. Does something like that seem like a good fit for this crate, or are there some reasons why it is not possible/not a good fit?