Crate versions this skill targets, what changed between the releases people still run, and how to migrate.
Check Cargo.lock before advising: many "it does not compile" questions are version mismatches.
Contents
- Version matrix
- axum 0.6 -> 0.7
- axum 0.7 -> 0.8
- tower and tower-http
- tokio 1.x
- tokio-util
- Rust editions and language features
- Mismatch symptoms
Version matrix
| Crate | Version used here | Pairs with |
|---|---|---|
| tokio | 1.x | everything below |
| axum | 0.8 | hyper 1, tower 0.5, tower-http 0.6 |
| tower | 0.5 | axum 0.8 (ServiceExt needs the util feature) |
| tower-http | 0.6 | axum 0.8 (each layer is a cargo feature: trace, timeout, cors, limit, ...) |
| tokio-util | 0.7 | CancellationToken (default), TaskTracker (feature rt) |
| hyper | 1.x | axum 0.7+, reqwest 0.12 |
| reqwest | 0.12 | hyper 1 |
| tracing-subscriber | 0.3 | features env-filter, json |
| sqlx | 0.8 | runtime feature runtime-tokio (+ a TLS feature for remote DBs) |
Mixing majors (tower-http 0.5 with axum 0.8, or two hyper majors) produces trait errors that look unrelated
to versions: "the trait Service<Request<Body>> is not implemented", "expected axum::body::Body, found
hyper::Body".
axum 0.6 -> 0.7
axum 0.7 moved to hyper 1.0:
axum::Server::bind(..).serve(app.into_make_service())is gone: useaxum::serve(tokio::net::TcpListener::bind(addr).await?, app).await?.- Request/response bodies are
axum::body::Body(no generic body type parameter on handlers and middleware). - Middleware written against
hyper::Bodymust be ported;tower-http0.5 pairs with axum 0.7. - Graceful shutdown:
axum::serve(..).with_graceful_shutdown(signal).
axum 0.7 -> 0.8
axum 0.8 (released January 2025) changes, in the order you will hit them:
| Change | Before (0.7) | After (0.8) |
|---|---|---|
| Path parameter syntax | "/users/:id", "/files/*path" | "/users/{id}", "/files/{*path}"; old syntax panics at startup ("Path segments must not start with :") |
| Custom extractors | #[async_trait] impl FromRequestParts<S> for X | plain impl FromRequestParts<S> for X with async fn (native async fn in traits) |
Option<T> extractor | any rejection became None | requires OptionalFromRequestParts/OptionalFromRequest; rejections now propagate |
Host extractor | axum::extract::Host | moved to axum-extra |
| WebSocket messages | String / Vec<u8> payloads | Utf8Bytes / Bytes |
| MSRV | 1.66 | 1.75 |
Migration steps: bump axum = "0.8", tower-http = "0.6", tower = "0.5"; run the service_hygiene scanner
(TK017 finds old paths); delete #[async_trait] on extractor impls; fix Option<..> extractors; run tests.
tower and tower-http
- tower 0.5 removed some re-exports and reorganised
ServiceExt; enablefeatures = ["util"]foroneshot/ready. - tower-http 0.6: every middleware is behind a feature. Newer 0.6 releases deprecate
TimeoutLayer::new(d)in favour ofTimeoutLayer::with_status_code(StatusCode::REQUEST_TIMEOUT, d). - Layer order in
ServiceBuilderis outermost first.TimeoutLayerrequires aDefaultresponse body, so putRequestBodyLimitLayerbefore it (outside), not after. - tower's own
tower::timeout::TimeoutLayerreturns an error instead of a response; with axum it needsHandleErrorLayer. Prefer tower-http's.
tokio 1.x
- tokio 1.x is stable: minor releases add APIs without breaking. It publishes long-term-support minor
versions (listed in its README); pin with
tokio = "~1.NN"only if your policy needs an LTS line. - Feature flags:
fullfor applications; libraries should enable only what they use (rt,macros,net,time,sync,signal,fs,process,io-util). Tests that pause time needtest-util. JoinSet(tokio::task) manages a dynamic group of tasks and aborts them on drop;JoinHandle::abortandAbortHandlecancel single tasks.tokio::task::spawn_blockinghas its own large thread pool (default up to 512 threads); CPU-heavy work belongs on rayon or a bounded pool instead.tokio-consoleneedsRUSTFLAGS="--cfg tokio_unstable"and theconsole-subscribercrate.
tokio-util
sync::CancellationToken: hierarchical cancellation (child_token,cancelled().await,cancelled_owned()for'staticfutures), always available.task::TaskTracker(featurert): track spawned tasks,close()thenwait()for graceful shutdown; unlikeJoinSetit does not abort on drop and does not keep results.codec(featurecodec):Framed,LinesCodec,LengthDelimitedCodec, the cancel-safe way to read framed protocols insideselect!loops.
Rust editions and language features
| Rust | Feature | Effect on async services |
|---|---|---|
| 1.75 | async fn and -> impl Trait in traits | async repository/service traits without async-trait (add a Send story) |
| 1.80 | LazyLock | static config/regex without once_cell |
| 1.82 | precise capturing use<..> | control what -> impl Future borrows |
| 1.85 | edition 2024; async closures (async |x| .., AsyncFn*) | RPIT captures all lifetimes; if let temporaries (lock guards) drop before else |
Edition 2024 if let rescoping helps services: if let Some(v) = map.lock().unwrap().get(k) { .. } else { .. }
no longer holds the guard in the else branch.
Mismatch symptoms
| Error | Likely mismatch | Fix |
|---|---|---|
Path segments must not start with ':' (panic at startup) | axum 0.8 with 0.7 paths | {id} syntax (TK017) |
the trait bound ..: Handler<_, _> is not satisfied | handler signature (extractor order, non-Send future, wrong body type) | #[axum::debug_handler] on the handler (feature macros) |
no method named oneshot | tower util feature off | tower = { version = "0.5", features = ["util"] } |
start_paused / time::pause not found | tokio test-util feature off | add it to dev-dependencies |
could not find TaskTracker in task | tokio-util rt feature off | tokio-util = { version = "0.7", features = ["rt"] } |
Two versions of http/hyper in cargo tree -d | crates on different majors | align axum/tower-http/reqwest majors |
rust-borrow-checker-error-explainer
Explain and fix Rust borrow-checker, ownership, move and lifetime compiler errors: E0382 use of moved value, E0499 cannot borrow as mutable more than once, E0502 also borrowed as immutable, E0505, E0506, E0503, E0507 cannot move out of index/Rc/reference, E0515 return reference to local, E0597 does not live long enough, E0716 temporary value dropped while borrowed, E0373 closure may outlive (spawn needs move), E0521 borrowed data escapes, E0384, E0596, E0594, E0381, E0713, 'lifetime may not live long enough', RefCell 'already borrowed: BorrowMutError' panics. Ranks fixes from restructuring (split borrows, entry API, split_at_mut, get_disjoint_mut, mem::take, Option::take, indices, thread::scope) down to clone and Rc<RefCell>/Arc<Mutex>; never unsafe. Covers NLL limits (get-or-insert), iterator invalidation, closure captures, self-referential structs. Ships std-only Rust triage tools.
rust-cargo-workspace-designer
Design, review and fix Cargo workspaces: when to split a crate (compile time, boundaries, publishing, proc-macros), crates/* vs apps/ + crates/ layouts, virtual manifest vs root package, members/exclude/default-members, resolver 2 vs 3 (MSRV-aware, edition 2024), [workspace.package] and [workspace.dependencies] inheritance, [workspace.lints], root-only profiles (lto, codegen-units), additive features with dep: and weak ?/ syntax, feature unification pitfalls, internal path + version deps, publish = false, Cargo.lock and MSRV policy, xtask, CI with --locked, nextest, rust-cache, sccache, cargo-hack/deny/machete/udeps/semver-checks, and cargo build --timings. Ships a workspace auditor and crate-graph tool.
rust-error-handling-thiserror-anyhow-designer
Design and review Rust error handling with thiserror 2 and anyhow 1: library vs application split, typed error enums with #[from], #[source], #[error(transparent)] and #[non_exhaustive], ErrorKind patterns, context via .context()/.with_context(), bail!/ensure!, source() chains, {:#} and {:?} reports, RUST_BACKTRACE/RUST_LIB_BACKTRACE, Box<dyn Error + Send + Sync>, main -> anyhow::Result or ExitCode, unwrap/expect/panic policy, Option to Result, collecting iterators of Results, axum IntoResponse mapping, retryable errors, tracing, async Send bounds, testing with matches! and downcast_ref, no_std core::error::Error, and snafu, eyre/color-eyre, miette, error-stack.
rust-lifetime-annotation-advisor
Decide when Rust lifetime annotations are needed and which ones: the three elision rules, structs holding references (struct Parser<'a>), owned vs borrowed (String, &str, Cow<'a, str>, Arc<str>), one vs several lifetimes, 'a: 'b bounds, T: 'static misconceptions, Box<dyn Trait + 'a> default object lifetimes, impl Trait + '_ and edition 2024 capture rules with use<..>, variance and PhantomData, HRTB for<'a> Fn(&'a T), GATs and lending iterators, async fn and tokio::spawn 'static, self-referential structs (ouroboros, self_cell, indices, arenas), Rc/Arc to escape lifetime soup. Ships std-only Rust checkers for redundant, missing, unused and over-restrictive lifetimes.
rust-trait-design-reviewer
Review and design Rust traits and generic APIs: generics vs impl Trait vs dyn Trait (static vs dynamic dispatch), dyn compatibility / object safety (generic methods, Self returns, where Self: Sized, error[E0038] not dyn compatible / cannot be made into an object), associated types vs generic parameters, blanket impls, coherence and orphan rule (E0117, E0119, E0210), newtype, extension, sealed and marker traits, supertraits and trait upcasting (1.86), async fn in traits (1.75) and the Send bound problem (trait_variant, async-trait), RPITIT, GATs, From/Into/TryFrom/AsRef/Borrow, std trait checklist (Debug, Clone, PartialEq, Hash, Default, Display, Error, Send/Sync, serde feature), #[must_use], #[non_exhaustive], semver hazards, Deref abuse, builders, typestate, PhantomData, Box<dyn Error + Send + Sync>, Fn traits, mockall.
rust-unsafe-block-auditor
Audit unsafe Rust for soundness: inventory every unsafe block, unsafe fn, unsafe impl/trait, extern block and unsafe attribute, check // SAFETY: comments and # Safety docs, and classify each as sound, needs-justification or likely UB, proposing safe alternatives first. Covers the five unsafe superpowers, aliasing (Stacked/Tree Borrows), invalid values, MaybeUninit vs mem::zeroed/uninitialized, transmute, slice::from_raw_parts, Vec::set_len/from_raw_parts, get_unchecked, CString::as_ptr dangling, unsafe impl Send/Sync, Pin projection, FFI (extern "C", repr(C), C-unwind, Box::into_raw/from_raw), strict provenance, static mut and &raw, edition 2024 (unsafe extern, #[unsafe(no_mangle)], unsafe_op_in_unsafe_fn, env::set_var), Miri, sanitizers, cargo-fuzz, loom, cargo-geiger and clippy safety lints.