Act 1 · A person refuses · 02
The Rust I didn't write

Below is the post exactly as published on LinkedIn, unchanged.
Rust is on this project's list of job-description keywords, and I wanted an excuse to write some. Wanting to is not a reason.
So the gate was simple. No Rust unless a profiler showed where it would go.
TL;DR — I went looking for the bottleneck that would justify it. The first fix made things forty times worse, and the profiler then showed there was no hot Python loop to rewrite. I wrote no Rust. What went into the record instead was the four conditions that would change that answer. The project answers questions out of aircraft maintenance manuals — one person, toy scale, synthetic XML.
Start with the boring option. If the work is CPU-bound, splitting it across processes should help, and an afternoon buys the answer. So the per-file validation got sharded across worker processes, and I went and read the numbers.
On package-a: serial run, 0.0019 seconds. Two workers, 0.078. Eight workers, worse again. Same verdict on all three sample packages. The work takes milliseconds, and starting a worker process costs more than the work does.
Next came the profiler, and that is where the real answer was. The CPU was in pyexpat parsing XML, in lxml building a schema, in pydantic-core validating models. Every one of those is already compiled. The Python around them was glue.
Which means there was nothing for a Rust extension to replace. You rewrite a hot Python loop in Rust. I did not have one. The top self-time in the entire profile is 0.04 seconds of XML parsing, and that is the total across twenty runs. py-spy was not installed on that machine, so this is cProfile only, and the profile output says so on its face.
So I wrote no Rust. And this is the part I nearly got wrong.
The obvious thing to record is "Rust: measured, not needed". That sentence is dead in six months. Nobody can act on it and nobody can argue with it, because it never says what would have to be true for the answer to flip.
So the record names four conditions instead, and the door stays shut while any one of them fails. A pure-CPU hotspot big enough to matter. Python fixes tried first, which is exactly what that forty-times-slower benchmark already bought. A stable boundary, data in and data out. Packaging cost under the benefit.
That record is also where I got something wrong. An earlier version of it named Tantivy and the vector store as Rust this project already used. It uses neither. The BM25 side is pure Python, the store is Vespa, and I approved the claim without checking. The real ones are pydantic-core and the HuggingFace tokenizers, which is how the project already gets Rust performance without writing any.
Which is the result I actually care about. Not that I avoided Rust. That if this corpus ever grows a real Python-side CPU bottleneck, the profiler gets re-run and I have to answer those four checks — not what I felt like building at the time.
A "no" nobody can re-open isn't judgment. It's a preference with a profiler attached.
What a reader takes away
A concrete defence against résumé-driven development: put the technology behind an evidence gate, and record the “not worth it” verdict as an artifact rather than as a silence.
Check it yourself
Each pointer opens a real file on the LearnArken repository’s main branch.