I learned something new about myself. I hate Rust.
I hate it more than C++. I did not think that was possible.
Painting: Albert Bierstadt, Mount Vesuvius at Midnight, 1868. Cleveland Museum of Art, CC0.
I learned something new about myself this month. I hate Rust.
I do not mean that it is hard, or that it has a learning curve. Everything has a learning curve. I mean that I have now written about 20,000 lines of it, and I hate it more than C++. I have used C++. I have seen a template error scroll off the screen. I still pick C++.
The 20,000 lines are a PDF editor that I am building, called T-Rustable. It runs offline, like most things I build. Yes, the name has the language inside it. I named it while I still liked Rust. That was not very long ago.
To write Rust, first install C++
On Windows, Rust has two toolchains. The GNU one is the easy one. It could not build Tauri, the app shell I chose, so I installed Visual Studio Build Tools 2022 and moved to the MSVC toolchain. The Build Tools are, for the most part, a C++ compiler.
That is only the start. The PDF work itself is done by PDFium, the PDF engine from Chromium, and by qpdf, which checks and writes the final file. Both are C++. The qpdf crate compiles qpdf from source, so a clean build of my Rust app first compiles about 200 C and C++ files. In my tests, that step alone took 36 to 61 seconds.
So the language that was going to save me from C++ needs a C++ compiler on every build machine, and it sits on top of a C++ engine. I am not saying it is a scam. I am saying that I noticed.
The safe language, with unsafe in it
There is a Rust crate for PDFium, pdfium-render. It is a good crate. It also keeps the raw PDFium document and page handles private. I needed those handles for a render that can pause and be cancelled while the document is being edited. I also needed to restart PDFium in the same process after a crash, and the crate can load PDFium only once per process.
So I wrote my own binding. Every PDFium function I use needs a declaration by hand and a safe wrapper around it. The app crates now have 54 unsafe blocks, and almost all of them live in that one module. They have tests. I keep them together, like a responsible adult.
But I came to Rust to stop writing unsafe code, and the first big thing I did was write a whole module of it.
The compiler is right, and it tells you
With C++, the deal is simple. The compiler lets you write the bug. You find it later, at night, in the program that is already running. It is a bad deal, but it is a quiet one.
Rust does not let you write the bug. It stops you, explains the problem in a paragraph, points at the line and suggests a fix. Most of the time the fix is correct. That is the problem. I cannot even be angry at it properly.
I want to run my program and see what happens. Rust wants me to understand it first. I did not ask for a teacher. I asked for a compiler.
C++ is dangerous, and honest about it. Rust is safe, and very pleased about it.
One new case broke every mod
Every tool in T-Rustable is a mod, including the built-in ones. A mod is a WebAssembly component, and wasmtime, which is written in Rust, runs it. My first plan for edits was one enum, with one case for each edit command. Then I tested what happens when the app grows.
| The mod was built for | The app gives it | Result |
|---|---|---|
| API 1.0.0 | 1.1.0, with one new function | The mod loads |
| API 1.0.0 | 1.1.0, with one new enum case | Refused: "a matching implementation was not found" |
One new case, and every mod built for the old version stops loading. To be fair, this is a rule of the WebAssembly component model and of how wasmtime links, not of the Rust language. I am including it anyway, because it is my blog.
The fix was to throw the enum away. Edits are now a transaction: an object with one method for each command. A new command is a new method, and old mods never see it. The price is that every type in the API is frozen. I can never add a field to a record again. I think about that sometimes.
Six days of dependency weather
The build runs cargo-deny, which fails when a dependency has a security advisory. The first 84 commits took six days. In those six days, it stopped me three times:
ttf-parser, which I used to check fonts for missing glyphs, was marked unmaintained in RUSTSEC-2026-0192. I moved toskrifa.ziphad to go to version 9 for three advisories.wasmtimehad to go to 49.0.4 for two security advisories.
I like that the tool exists. I do not like that I needed it three times in one week.
Then the fuzzer. cargo-fuzz builds for its own musl target by default, and the address sanitizer does not work with a static libc. One more flag. One more evening.
So why is it still in Rust?
Because it works, and I hate that most of all.
The editor opens a PDF, draws it in tiles and lets mods add text, shapes and images, with undo. A mod that crashes takes down the mod, not the app. If the engine panics, it restarts PDFium and reloads the open documents from their journals. A 24-step self-test runs twice on Windows and twice on Linux in CI.
Every one of those guarantees came from the compiler refusing something I wanted to do. It was right every time. You cannot hate something properly when it keeps being right. I am trying anyway.
So T-Rustable stays in Rust. I will keep writing it, and I will keep hating it, more than C++. It is early, and not ready for daily use yet. If you want to watch me suffer in public, the code is on GitHub.
The work this is about
T-RustableA local, offline PDF editor for Linux and Windows. Every tool is a mod, and you can write your own. Source-available, in early development.


