rust

A weekend with Rust, and the index that stopped cloning

A weekend with Rust, and the index that stopped cloning

Rust has been on my list for years in the way that things stay on a list: I read about it, agree that it sounds correct, and then reach for something I already know. This month I finally sat down with it for a weekend and wrote something small that does real work — a tool that walks a directory of markdown files, parses front matter, and reports on it.

These are beginner's notes. I am writing them because the things that surprised me were not the things I had been told would surprise me.

The first five minutes got better

A small thing first, because it was the first thing I noticed.

Cargo has historically kept its index of crates.io as a git repository, cloned locally. That repository indexes every crate in existence, and it had reached the size where updating it was a visible pause before anything else could happen.

Rust 1.68, in March, stabilised a sparse registry protocol: instead of cloning the whole index, Cargo fetches over HTTP only the entries for crates you actually depend on. It is not the default for crates.io yet — that is planned for 1.70 — so you opt in:

export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
cargo build

The difference on a cold machine is not subtle. It is a small piece of infrastructure work that changes the impression the language makes in the first minute, which is worth more than it looks.

What the borrow checker is actually about

I had absorbed the idea that Rust's ownership rules are about memory safety, and they are, but that framing made them feel like a tax for a problem I do not have. I write C# and TypeScript; I have a garbage collector; memory is not what breaks.

What made it click was realising the rules are about aliasing, and that aliasing bugs are entirely familiar. One reference mutating a structure while another reads it is not a C problem. It is the object passed into a function that mutated it, the array a caller kept a reference to, the shared configuration that one module edits at startup. I have debugged all three in garbage-collected languages, and none of them were memory-safety bugs. They were correctness bugs, and they were hard to find because nothing in the code said who was allowed to change what.

Rust makes that a compile error. One mutable borrow, or any number of immutable ones, never both. Once I stopped reading that as a memory rule and started reading it as "say who owns this", the errors stopped feeling arbitrary.

The other half is that ownership is where cleanup happens — a value is dropped when its owner goes out of scope, so files close and locks release without a using or a defer, and without a collector deciding when.

The compiler deserves its reputation

error[E0502]: cannot borrow `posts` as mutable because it is also borrowed as immutable
  --> src/main.rs:34:5
   |
32 |     let first = &posts[0];
   |                  ----- immutable borrow occurs here
34 |     posts.push(next);
   |     ^^^^^^^^^^^^^^^^ mutable borrow occurs here
36 |     println!("{}", first.title);
   |                    ----- immutable borrow later used here

Three locations, named in the order that explains the problem: where the borrow started, where it conflicted, and why the first one was still alive. I have spent a lot of my career with compilers that tell you a type does not match and leave the rest as an exercise. This is a different standard, and it is most of why the learning curve is survivable.

The thing nobody warned me about is that the fix is usually not clever. My first instinct on every error was to reach for .clone(), and that works, and it is almost always the wrong answer. The right answer is nearly always to restructure so the two things are not live at the same time — take the index instead of the reference, or finish reading before writing. Every time I did that, the code got simpler. That was the genuine surprise of the weekend: the borrow checker was mostly telling me my structure was muddled, and it was right.

Where I actually landed

Not converted, and not planning to rewrite anything. The languages I reach for are the ones where the ecosystem is already pointed at the problem I have, and for a backend service that is still C# or TypeScript, with Go when I want a static binary.

What I got is a better vocabulary. "Who owns this value and who is allowed to change it" is a question worth asking in every language, and Rust is the only one I have used that refuses to compile until you answer it. I have caught myself asking it in C# since, which is a reasonable return on two days.

The other thing I will keep is the compiler errors as a standard to hold other tools to. There is no technical reason a TypeScript error could not point at three locations and explain which one is still live. Rust demonstrates the bar, and once you have seen it, everything else looks like it is not really trying.

Deyan Peev

Written by

Deyan Peev

Founding Engineer · Sofia, Bulgaria

Deyan Peev

Founding Engineer in Sofia, Bulgaria. Currently at 1club.

Elsewhere

© 2026 Deyan Peev