Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Standard Library and Ownership

The public ergonomics depend on ordinary Rust ownership rules being made visible at a few deliberate boundaries.

What the caller owns

  • The backend value is moved into WorkerBuilder::backend, then into the worker.
  • The handler function or service is moved into build.
  • Each job’s Args is moved into the handler’s first argument.
  • Values passed through .data(value) must be Clone + Send + Sync + 'static. The layer clones the value into task extensions; large shared state is usually Arc<State>.
  • Extractors inspect &Task briefly, but return owned values for the handler.
  • WorkerContext is cheap to clone because its shared internals use Arc.
  • Retry policies clone Task, so retryable payload, connection, and identifier types must satisfy the corresponding Clone bounds.

The user normally does not lend &mut application state across .await. If shared mutation is necessary, the state explicitly contains a concurrency primitive such as Arc<Mutex<T>>, a pool, or a channel sender.

Small standard-library tools with architectural weight

Arc lets cloned tasks and contexts share execution metadata. Atomics represent monotonic shutdown, status, and in-flight counters without one giant lock. Mutex<Option<Waker>> stores the wakeup needed to turn shutdown state into a Future. PhantomData lets WorkerBuilder carry its not-yet-runtime type state without storing dummy payloads. Pin and pinned drop make task accounting safe when futures are cancelled.

Clone is a policy, not a convenience

Cloning an Arc<App> shares identity cheaply. Cloning a Task for retry means replaying its payload is semantically permitted. Apalis puts that distinction in trait bounds: a non-cloneable task can execute once, but cannot silently enter a retry mechanism that requires a second owned request.