Back to Blog

Rust & AI Weekly #13: the counters looked fine

Decebal D.
September 21, 2026
23 min read
Rust & AI Crate RadarExplore

3D cutaway of a graphite flight recorder: green status lights above copper event channels, with a queue stalled inside.

The counters looked fine. The queue told another story.

Welcome back to Rust & AI Weekly, the curated, vetted Rust newsletter on the crates and tools showing up where Rust meets AI. Today's issue: dial9 records what Tokio is actually doing in production and no longer needs tokio_unstable to do it, the Rust project warns that prominent maintainers are being targeted through fake job calls, and This Week in Rust's Crate of the Week is a JPEG codec most companies cannot use without a licence conversation. The theme this week is the counters looked fine: every story below is a case where the summary view said healthy and the underlying record said otherwise. A runtime dashboard with idle workers and full queues. A LinkedIn profile that passes a glance. A freely available codec whose commercial terms still need a read. A release plan with no further breaking changes expected, followed by two breaking release candidates. Last week I asked you to find out who pays for each dependency. This week the ask is smaller and harder: stop reading the aggregate and go look at the log.

(Status lines reflect public signals as of September 22, 2026; stars and downloads are approximate and move fast.)

Rust & AI Crate Radar, tools mapped by Adopt / Trial / Assess / Hold, this week's snapshot

This week's radar: six entries, three new and three updates, bringing it to 80 tools: 14 Adopt, 33 Trial, 31 Assess and 2 Hold. Explore the interactive version.

The week's anchor

On Thursday, September 17, the Rust blog published Be alert: targeted attacks on prominent Rustaceans, written by Adam Harvey for the crates.io team and the security response working group. The claim is specific: there is an ongoing campaign against rust-lang members and owners of popular crates, and the goal is to compromise their machines and accounts in order to publish malware under their names. The vector is not a phishing email. It is a video call about something good, a job, a contract, a collaboration, during which the target is asked to install a "missing audio codec" or run a command that has been placed on their clipboard. The attackers build new but plausible company profiles, LinkedIn presence included, so the cursory check comes back clean. The post links a June write-up by Matt Mastracci of a failed attempt of this shape against many prominent Rust developers, notes that the arrayref compromise last month came through a similar route, and says plainly that nobody yet knows whether these are one campaign. The pattern is documented as DPRK tradecraft and is not Rust-specific; Go Weekly ran the same alert to its readers on Friday.

The advice is unglamorous and correct: be the one who sets up the call, on a platform you already use; recheck MFA and login history; write to help@crates.io or security@rust-lang.org if anything looks off. The reason it belongs at the top of an issue about crates is that the last four issues of this newsletter have been about supply-chain controls that operate on the artifact: cargo-vet audits, cargo-acl capability checks, min-publish-age, the packaged lockfile on install. This attack goes around all of them. It compromises the person, and a person with publish rights produces a perfectly signed, perfectly audited-looking release. If you maintain something with more than a handful of dependents, this post is for you. If you depend on such people, and you do, the mitigation available to you is the one from issue #10: a publish-age delay and a build-time capability check, so that a compromised maintainer's release has to sit in public for a few days and cannot open a socket from a build script when it lands.

Pick of the week

dial9 0.5.1 — a flight recorder for Tokio, and the crate I have had on the series plan since March without a release worth leading with. Now there is one. Russell Cohen built dial9 to debug a service at AWS that fell over at 90% CPU while its runtime metrics insisted the workers were idle and the queues were full at the same time; the metrics were aggregates, and aggregates cannot tell you that the kernel took 18 milliseconds to schedule worker 47 after Tokio asked for it. dial9 records the events instead: every poll start and stop, every worker park and unpark with kernel timestamps, wake causality, sampled allocations with liveset tracking, frame-pointer CPU profiles, tracing spans, getrusage and TCP accept-queue depth, all into a rotating disk or memory buffer, or straight to S3, with overhead the project's March introduction described as typically under 5%. That is the project's measurement, not an independently verified promise for your workload. The 0.5 release on August 26 (0.5.1 on September 17, this week) is a restructure rather than an increment: the crate was renamed from dial9-tokio-telemetry to dial9, Tokio became one Source among several so the profilers no longer pull in tokio at all, spans can be produced natively with dial9_span! and a Tower layer rather than through your tracing subscriber, a trigger mode keeps a ring buffer and flushes only when your code says something looks wrong, and the viewer can aggregate flamegraphs and span histograms across many trace files from many hosts and diff a slow operation against a fast one. The 0.5.1 release notes add system information in trace segments, FreeBSD telemetry, off-CPU and spawn-to-first-poll points of interest, and fixes for task dumps, rotation and liveset reallocations. They also deprecate RecorderSourceExt methods in favour of inherent RecorderBuilder methods and mark sealing the extension traits as breaking. The line I want you to notice, after last week's tokio_rcu entry, is this: dial9 0.5 works without tokio_unstable. It degrades, since task spawn and terminate events and per-worker queue depth need the runtime hooks and poll coverage narrows to tasks started through dial9's own spawn helpers, but it compiles and runs on a stock toolchain, which is the difference between a tool your platform team can adopt and one they have to get a rustflags exception for. Go deeper with Cohen's Principles for fast Tokio applications, written on the way home from the RustConf Unconf and listed in This Week in Rust 669: work backward from a real metric before you fix any long poll, treat schedule latency as the number that matters, batch for throughput and yield for latency (his mini-Redis example cuts p99 from 2.548 ms to 0.320 ms by yielding after four consecutive ready reads), keep every std mutex critical section to a hashmap update because one contended lock can stall all four workers at once, and reserve cores for the runtime rather than giving Tokio all of them. It is the best short document on production Tokio I have read this year, and it is explicitly a first draft with a PR link.
Maintenance: actively maintained (Russell Cohen and Jess Izen; the crate is co-owned on crates.io by Carl Lerche; eighteen named contributors on 0.5 including David Tolnay); Apache-2.0; ~500 stars, 534 commits · Latest: v0.5.1 (Sep 17, 2026); 0.5.0 Aug 26; ~750k downloads under the old crate name · Adoption: Trial; AWS services were the early adopters and Ditto runs the CPU profiler on production Android behind a flag; the cost is a 0.3-to-0.5 migration guide, force-frame-pointers for profiling, and tokio_unstable if you want every task; the exit cost is close to zero because telemetry is not in your data path

Codecs

zenjpeg 0.8.4 — This Week in Rust 669's Crate of the Week, suggested by Kornel, and the entry this week where licensing belongs beside the performance table. The code is impressive. Lilith River at Imazen started it as a port of Google's jpegli, rewrote it six times, and ended up with an independent pure-Rust JPEG encoder and decoder under #![forbid(unsafe_code)], with SIMD through her archmage safe-token crate rather than raw intrinsics, streaming single-pass encode and decode with bounded memory, decode limits on pixels and bytes so a hostile image cannot exhaust a server, cooperative cancellation tokens, parallel decode when restart markers are present, adaptive and trellis quantization, XYB, UltraHDR gain maps, and JPEG-to-JPEG recompression. The author's published numbers on a Ryzen 9 7950X, with a pinned-commit reproduction in the repo, not independently rerun here: baseline decode at 0.94x libjpeg-turbo, progressive decode 1.35x faster because the pipeline is fused, a 4096px parallel decode in 13% of the C time, and encode beating mozjpeg at matched file size on 81% of a 337-photo corpus. The README discloses that it was developed with Claude and tested against the C++ reference with 930-plus tests. For anyone building an image-ingestion path for a vision model, this is the shape you want: no C toolchain, no unsafe, limits by default. Now the LICENSE. zenjpeg is dual-licensed AGPL-3.0 or commercial, with a $1 startup licence under $1M revenue and fewer than five employees and a sliding-scale subscription above that. River is candid about why: she has maintained this ecosystem full-time since 2011 and dual-licensing is how it stays funded without venture capital. That is a legitimate model and, after last week's issue, a refreshingly explicit answer to "who is paying for this." It is also the reason this is Assess and not Trial: for most companies the AGPL is a conversation with legal before it is a conversation with engineering, and the decoder API is still marked prerelease with breaking changes expected. zune-jpeg (0.5.16-rc2 on September 8) and the image crate remain the permissively licensed defaults.
Maintenance: actively developed, effectively solo but funded (Lilith River / Imazen); 2,599 commits; 15 stars · Latest: v0.8.4 (Jun 1, 2026), the fourteenth GitHub release; formerly published as jpegli-rs · Adoption: Assess; read LICENSE-COMMERCIAL before you read the benchmarks, and if the licence works for you this is the most complete safe-Rust JPEG codec I have seen

SIMD

fearless_simd 1.0.0 — an update to issue #8's Trial entry, and my carry-over item from #11 finally resolved. The August 0.7 post targeted 1.0 for early September if no API concerns emerged, with no further breaking changes planned. It did not promise an API freeze. Two release candidates followed: rc.1 on September 13 and rc.2 on September 19, both cut by LaurenzV with Shnatsel writing nearly every change. Both contain breaking changes. Final 1.0.0 shipped on September 21, alongside fearless_simd_macros 0.1.0. rc.1 renamed N to LEN and reshaped the as_array family to match std::simd, moved abs down to SimdBase so integers get it too, and added reductions including reduce_sum, reduce_product, reduce_min and reduce_max, with precise min/max variants. Floating-point sum/product reductions use a fixed evaluation order and agree across backends for a given vector type and lane count, except for NaN bit patterns. It also added mul_add_precise with a single-rounding guarantee even without hardware FMA. rc.2 moved witness() to a new ExtractToken supertrait and renamed it token(), and shipped a companion fearless_simd_macros crate whose #[simd] attribute runs a generic function body with the right target features enabled. Two things here are the kind of record I keep asking you to read instead of the badge. First, rc.1 documents the storage representation of vector types and promises it will not change without a semver major. Second, the project adopted a security policy: from 1.0, the latest release for each MSRV gets security backports for at least three years after that Rust version shipped. That is a written stewardship commitment, and it is rarer than a 1.0. Verdict holds at Trial, with one adjustment: trial the final 1.0 release and test the migration from 0.7 against those API changes. The August plan described intent; the release history records what actually shipped.
Maintenance: actively maintained (Linebender; Shnatsel writing, LaurenzV releasing); ~458 stars; MIT OR Apache-2.0; security policy with a three-year backport window per MSRV · Latest: v1.0.0 (Sep 21, 2026); rc.2 Sep 19, rc.1 Sep 13; requires Rust 1.89 · Adoption: Trial, unchanged from #8; 1.0 arrived September 21 after two breaking release candidates; review the migration before upgrading

Concurrency

tokio_rcu 0.2.1 — last week's Crate of the Week did not sit still. Roee Shoshani shipped 0.2.0 on September 15 and 0.2.1 on September 17, stars went from 8 to 38, and commits from 157 to 226. The commit history and versioned docs make the changes clearer than the current README: rcu_block_on and enable_rcu already existed in 0.1.3. The API rename was rcu_ptr::RcuPtr to rcu_box::RcuBox. The 0.2 work removed per-thread live-guard reader bookkeeping, added an include_calling_thread argument to synchronize_rcu, and reorganized and reran the benchmarks. Version 0.2.1 then tightened RcuBox's Sync bound to require T: Send + Sync, fixed a stall involving blocking tasks, and added branch hints. This is not a correction to last week's three-times-faster figure: that was the previous README's claim. The refreshed i7-12700 table reports mean read-only times of 2.166 to 3.496 ms for RcuBox versus 23.87 to 132.9 ms for arc-swap over 1, 8, 16, 32 and 64 tasks; the README describes that as reads 9 to 40 times faster and writes twice as slow. PR 6 renamed the existing Divan benchmark functions, without changing their workloads in that diff, after the implementation changed. It is not a new harness, nor a measured 9-to-40-times gain over 0.1.3. These remain the author's results, not independently rerun here. What did not change is the gate in the 0.2.1 docs: tokio_unstable is still required. Read this next to dial9 above. dial9 made the hooks optional by accepting reduced coverage; this RCU primitive relies on on_after_task_poll for its correctness argument. Assess holds.
Maintenance: brand new, solo (Roee Shoshani); 226 commits; 38 stars; MIT · Latest: v0.2.1 (Sep 17, 2026) · Adoption: Assess, unchanged; the required on_after_task_poll hook remains unstable; whether the Tokio team has answered last week's question is unverified this run

GUI

Slint 1.18 — the issue #3 Deep Dive subject shipped 1.18.0 on September 16, listed in TWiR 669. The headline is FlexboxLayout, which brings the web's layout model into the Slint DSL with wrapping items and a familiar API, so a team that knows CSS can lay out a native panel without learning a second mental model. Also: z-order changes at runtime (long requested), spring animations alongside easing curves, animating and orienting an item along a path, push, remove and insert on arrays and models, custom default values for struct fields, a WindowMoveArea element for custom title bars, screen-reader support for text inputs, an experimental Vello-based renderer, and a compiler that emits leaner code for faster builds and smaller binaries. Large-text rendering and editing got much faster. For this newsletter the relevance is the same as in July: Slint remains the embedded-and-desktop toolkit with an MCP server built in, so an agent can drive a running UI, and every release like this one widens the set of apps you would plausibly build that way. Verdict holds at Trial; the tri-licence still needs a legal read, which makes it the second entry this week where the licence is part of the evaluation.
Maintenance: actively maintained (SixtyFPS GmbH; NLnet-funded features); ~23.9k stars · Latest: v1.18.1 (Sep 21, 2026); the feature release above was 1.18.0 (Sep 16) · Adoption: Trial, unchanged from #3

Data plumbing

html2text 0.17.1 — surfaced by the newsletter scan. The draft's lib.rs trending claim is unverified this run: html2text is absent from the current list. Go Weekly 618 listed k3a/html2text 1.5, a zero-dependency Go converter from HTML to plain text. The Rust crate of the same name, by Chris Emerson, has been doing the harder version of that job since 2016: it parses with html5ever and then runs a real layout pass, so tables render as tables, lists as lists, links get numbered footnotes, and text wraps to a width you choose, with rich and coloured output modes for terminals. If you are building a RAG ingestion path, the difference is not cosmetic. Flattening a pricing table into a paragraph destroys the structure your retriever would have matched on; rendering it as aligned columns keeps it. About 6.1 million all-time downloads on crates.io and about 521,000 downloads per month on lib.rs, MIT, and one primary maintainer since 2016, alongside other contributors. Trial rather than Adopt for that last reason, and because 0.x versioning still moves the API between minors.
Maintenance: maintained, one primary maintainer (Chris Emerson / jugglerchris); ~245 stars; MIT · Latest: v0.17.1 (Apr 19, 2026) · Adoption: Trial; the Rust seat opposite Go's html2text, and the more capable of the two

Language watch

  • Allocator is heading for stable. PR 156882 by nia-e, "alloc: stabilise Allocator", finished its final comment period with a disposition to merge and is still open as of September 22, awaiting the merge itself. This is the trait behind custom allocators for Box, Vec and friends, unstable since 2020; if it lands, cetra3's allocator survey from last week stops being a nightly story.
  • Merged stabilization work: core::mem::DropGuard (PR 161520 by Yoshua Wuyts, merged Sep 13, closing last week's FCP item), Vec::from_fn (PR 162685, merged Sep 12), unsafe_cell_access (PR 162504, merged Sep 14), and the RISC-V d and f target features (PR 161385 by Ralf Jung, merged Sep 13). Separately, Thread::os_id (PR 160219, merged Sep 9) was implemented as an unstable API, not stabilized. Also merged and now confirmed in TWiR's Cargo list: cargo install uses the packaged lockfile by default (PR 17388).
  • In final comment period: funnel_shifts including const (PR 161015), Result::into_ok and into_err (PR 161712), the WebAssembly wide-arithmetic target feature (PR 160877 by Alex Crichton), and, in Cargo, build.profile and install.profile config keys (PR 17215 by Ed Page) so a team can default cargo build to a custom profile without wrapping the command. The 8-byte RawWakerVTable alignment guarantee (PR 158186) finished FCP with disposition merge and is not yet merged.
  • Compiler performance: Kobzol's September 14 triage reports zero primary regressions and 199 primary improvements (mean -0.7% in instruction counts), several of them reverts of earlier regressions, with PR 162422 bringing Polonius closer to the old NLL borrow checker's speed.

In brief

Rust is a Tier-1 language at Microsoft — Victor Ciura's Rust Foundation guest post (Sep 10) puts Rust alongside C++, C# and TypeScript for internal development, and the technical artefact is rustc_codegen_utc, an alternative rustc backend in the same family as rustc_codegen_gcc and cranelift that emits through the MSVC backend, production-ready since early 2026, self-hosting since Rust 1.90, and used by more than 100 Microsoft repositories; that is two custom rustc codegen backends in two weeks after cuda-oxide, and Rust Bytes #136 included this in its link roundup, so I am filing it rather than leading · Rust Foundation adds Solana Foundation and NVIDIA as Platinum members — announced September 9, alongside the RustConf week of NVIDIA's CUDA Rust commitment, which is the funding half of last week's lead · Developing provably correct Rust code with Verus — Bryan Parno at Amazon Science on the SMT-backed verifier that issue #11's Kani entry listed as "under review" for the std verification effort · Optimizing a single Clippy lint by 3133x by Alejandra González, also linked in Rust Bytes #136 · Shipping Rust static libraries without symbol collisions by Stephan Eckes at ai-coustics, whose Rust audio SDK hit collisions between symbols bundled into static libraries; the draft's specific ring attribution is unverified this run · CO3: Toward the Optimal FFI by mversic · Why building a Rust LSP is hard from the Rust Glancer project · A visual guide to Rust async by Henrik Åkesson, a good pairing with dial9's principles post · Operators of death: checked arithmetic in Rust by John Arundel · Rust generics: from static to dynamic dispatch by Sylvain Kerkour · Trying to make a loop auto-vectorize by jsgroth, which is the problem fearless_simd exists to make unnecessary · One Lock to Rule Them All by FlakM on a PostgreSQL advisory-lock wrapper inspired by sqlx migrations · and lib.rs's notable-releases list this week included wasmtime, clap, crypto-bigint, cudarc, syd, picoserve, postcard-rpc and vello, unverified this run as a historical list; the live list has since changed, and I did not verify those releases individually.

Elsewhere

  • Go shipped a GPU-accelerated native IDE; the Rust seat at that table has been occupied for two years. Go Weekly 618 led its tools section with Rune by Ernest Romero Climent, a native IDE built on a fork of Ebitengine, now GPLv3 with about a thousand stars, and the interesting engineering note is how they got the integrated terminal close to Alacritty and Ghostty speed without cgo on the hot path. The Rust counterpart is Zed, which has been on this radar at Adopt since issue #1 and whose GPUI renderer serves the same role in a different implementation. Two languages, the same conclusion: if you want a native editor that feels native, you write the renderer yourself.
  • Go has two wasm-to-Go compilers now. Rust's wasm2rs has 47 downloads. Go Weekly also listed goccy/wasm2go by Masaaki Goshima, which translates a Wasm binary into standalone Go source plus amd64 and arm64 assembly, alongside the older ncruces/wasm2go that powers the cgo-free go-sqlite3. The use case is real: ship a C library built with the WASI SDK inside your binary without carrying a Wasm engine. The Rust answer is wasm2rs by hirosassa, at 0.1.1 from August with a few dozen downloads. Rust's usual answer is "just link the C library," which is true and also the thing the Go tools exist to avoid. A mature Rust counterpart remains an unverified gap this run: these projects were checked, not the entire ecosystem. Keep it as a research lead, not a claim that no alternative exists.
  • Two HTML-to-text libraries, one name, different ambitions. The pairing is in the Data plumbing entry above: Go's k3a/html2text is a dependency-free flattener; Chris Emerson's Rust html2text is a layout engine. If your RAG chunks come from HTML, the second one is the one that keeps your tables.
  • Go Weekly carried the Rust maintainer alert to another ecosystem. Issue 618 ran it in "In brief" on September 18 and warned that Go is not immune. Rust Bytes #136, published September 15, led with the debugging survey and included Microsoft's Tier-1 post in its roundup. Its full body contains none of dial9, the Tokio principles post, zenjpeg, the targeted-attacks alert, fearless_simd, tokio_rcu, Slint 1.18 or html2text. It predates the September 17 alert, so that absence says nothing about how quickly Rust readers heard the warning. The attack pattern crosses ecosystems, and the defence starts with the maintainer.

A thought for the week

Aggregates tell you that something is wrong. Only the record tells you what.

dial9 exists because a set of perfectly good Tokio metrics described a service as simultaneously idle and overloaded, and both were true and neither was useful. The counters were not lying. They were summarising, and the thing that mattered, an 18 millisecond gap between "wake worker 47" and worker 47 waking, lives below the resolution of any counter. It took an event log to see it. That is the technical version of this week's theme, and the rest of the issue is the same shape with different nouns. A LinkedIn profile is a counter: it summarises a company into a plausibility score, and the Rust project's alert says attackers now clear that bar on purpose. A crates.io page is a counter: version, downloads, a licence badge. zenjpeg's registry page correctly lists AGPL or commercial licensing. That badge tells you the choices; the README and commercial terms explain eligibility and obligations. A release plan with no further breaking changes expected is a summary of the maintainers' intentions; the record is two breaking release candidates followed by final 1.0 on September 21.

The leadership version of this is that you already have dashboards, and your dashboards are why incidents take so long. Not because the dashboards are wrong but because the review meeting stops at them. Green means "nobody looked further." The organisations I have seen debug well share one habit: when the summary and the symptom disagree, they go get the log, whether that is a dial9 trace, a git log on the dependency, the actual LICENSE file, or a five-minute check of whether the recruiter's company existed last quarter. It is slower per question and much faster per incident.

So, three concrete things. Put an event recorder in your async services before you need one, because you cannot install it during the incident; dial9's trigger mode makes the storage cost negligible until the moment it is not. Add "read the LICENSE and SECURITY.md" to your dependency checklist as separate line items from "check the licence badge," because zenjpeg and Slint require a read of the actual terms, and fearless_simd puts its support commitment in SECURITY.md. And if you have publish rights on anything people depend on, treat every unsolicited call as an incident until you have set it up yourself, on your own platform. The counter says you are fine. Go look.

Before I go

Housekeeping on last week's open threads. fearless_simd 1.0 shipped on September 21 after two breaking release candidates, so that carry-over closes. tokio_rcu moved to 0.2 and gained thirty stars in a week, and the required on_after_task_poll hook remains unstable; if you are on the tokio team, I would still rather report your answer than guess. RustConf recordings were not yet published where I could find them this run, so Melih Elibol's and Joe Birr-Pixton's talks from last week's issue remain the pairings to add when they land. I read Rust Bytes #136 in full: its September 15 lead was the debugging survey (which I ran as an In brief last week), its spotlight was rust-smallvec, and its roundup included Microsoft Tier-1, Cargo team changes, Rustls, Dioxus and the Clippy optimization. None of this issue's six crate entries or the dial9 principles post appeared there. The archive still showed #136 as the latest issue on September 22.

Two things I would like to hear about. First, if you run dial9 in production, especially outside AWS: what did the overhead look like, and did trigger mode change how much you keep? Second, if your legal team has actually evaluated an AGPL-or-commercial Rust dependency, how long did it take and what did they ask for? Both answers would make better entries than anything I can find on GitHub.

That's the issue. Got a Rust+AI crate or tool I should feature next week? Reply and tell me; reader picks shape the list.

Keep shipping, Decebal

Rust & AI Crate Radar

Every tool I feature, mapped by verdict· 80 tools

Decebal Dobrica

Let's Connect!

Have questions or want to discuss this further? I'd love to hear from you.