Adding a sleep to a Rust Axum handler caused a compiler error (puzzler)

To check that a spinner appears and vanishes on a web page, I added a sleep to a request handler to slow it down. My handler then no longer compiles due to a type error. The signature is unchanged. How can adding a sleep do that?

I’m using axum and the code is:

pub fn routes() -> Router { 
Router::new().route(“/lazy/users”, get(users))
}
async fn users() -> Response { 
// Generate 8 random users:
let mut rng = rand::thread_rng();
let users = User::randos(&mut rng).take(8);

// Pretend this takes ages:
tokio::time::sleep(std::time::Duration::from_secs(4)).await;

// Render a table of users:
html! {
table {
thead { (User::render_header()) }
tbody { @for user in users { (User::render_row(user) } }
}
}.into_response()
}

Without the sleep there’s no problem, and all works as expected. With the sleep I’m told my users function no longer matches the type bounds for a get request:

error[E0277]: the trait bound `fn() -> impl std::future::Future<Output = Response<Body>> {users}: Handler<_, _>` is not satisfied
--> src/apps/lazy/mod.rs:12:35
|
12 | .route("/lazy/users", get(users))
| --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn() -> impl std::future::Future<Output = Response<Body>> {users}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<MethodRouter<S> as Handler<(), S>>
<axum::handler::Layered<L, H, T, S> as Handler<T, S>>
note: required by a bound in `axum::routing::get`

What’s going on? If you look into the Handler trait, you’ll see that the output type, which is a future, needs to be Send.  A type is Send only if it can be sent to another thread. This is needed because async functions in Rust can moved between threads at await points.

By adding an await the compiler enforces that all captured values, which might execute on a different thread, need to be Send. The sleep isn’t the real problem.

I’m using the standard thread local random number generation two lines above — and that’s not Send — and hence, a compile error.

To fix this, I could change to a different random number generator. What I actually did was move the sleep to the start of the function, before the random number generator.

From what I can recall, a future in Rust captures state inside a struct. I would assume the fields on the struct require Send, which is why moving values and their usage before the await (future creation) resolves this.

Uploaded image
Behold the beauty of my lazy loading list of random-ish users.