85 bits

code; learn; share;

building an atomic bomberman clone, part 9: i know rust

Tags = [ bomberman-series, rust, gamedev, multiplayer ]

In part 1 I wrote that I wanted to learn Rust by building something real. That I'd use AI as a mentor. That the measure of success was whether, at the end of it, I could honestly say "I know Rust."

Nine posts later, I can.

I know Rust

Not in the "I could pass a Rust interview tomorrow" sense. In the "I now catch JavaScript bugs by thinking like the borrow checker" sense. The moment it actually landed was debugging client-side prediction in part 8. I reassigned a buffer reference in TypeScript and my brain said no, that's a dangling borrow. JavaScript doesn't have borrows. But the pattern-recognition was already there.

That's the point. Not syntax. Intuitions.

the numbers

  • 18 days from first commit to last. 15 of those were actual working days (nights actually).
  • 74 commits across the server, client, shared protocol, and docs.
  • 2,295 lines of Rust across 10 modules.
  • 1,220 lines of TypeScript across 16 client files.
  • 9 journal entries that became 8 technical blog posts (plus this one).

Not huge numbers. But every line was fought for. Every Arc<Mutex<T>> got picked apart. Every match on a GameState enum came from thinking about what should exist in which phase. The code is small because I refused to write code I didn't understand.

what rust actually taught me

Not the syntax. I can google syntax. Three things moved into my bones:

The borrow checker as a thinking tool. Ownership isn't just a compiler thing. It's a way of asking "who is allowed to change this, and when?" That question turns out to be useful in every language. The stale array reference bug from part 8 is the clearest example. A module held a reference to an array that had been reassigned out from under it. Classic dangling reference. Rust would have caught it at compile time. My JS brain caught it too, because I'd been trained.

Enums-with-data as the default way to model state. In TypeScript you'd use a discriminated union. Close, but nothing stops you from accessing a field that only exists in one variant. In Rust, match forces you to handle every case, and fields that don't exist in a variant literally don't exist. Once you've modeled state this way, going back feels sloppy.

"If it compiles, it's probably right" is real. I didn't believe it when people said it. Now I do. Not because the compiler is magic, but because by the time you've satisfied it, you've already answered every question about lifetimes, ownership, and error handling that the code implies. The kind of bug left over is usually a logic bug, not a wiring bug.

the AI-mentor experience

Claude wrote zero lines of the production code. That was the rule. Claude explained concepts, proposed architectures, reviewed code, debugged at the wire. I typed. (I cheated a little and had Claude write some tests when I got lazy.)

What worked:

  • Concept explanations in JS terms. "An Arc<Mutex<T>> is the Rust equivalent of a shared mutable reference, except you have to be explicit about both sharing and mutation." That framing unlocked me faster than any Rust Book chapter.
  • Debugging heuristics. In part 6, when I saw ghost explosions, Claude didn't guess. It suggested "start at the wire." Turned out to be a visual rendering quirk, not a data bug. The investigation structure was the real value.
  • Spec writing. ADRs, phase plans, protocol definitions. Claude is relentless about writing down the "why" before the "what." I built a docs/ folder I can still read 18 days later and understand.

What didn't:

  • Writing idiomatic Rust on the first try. Claude would suggest imperative loops that worked. They passed tests. They were also not how Rust people write Rust. I had to learn the idioms by reading the standard library and asking "is there a shorter way?"
  • Replacing understanding. Any bug I fixed by pasting into Claude and pasting the answer back came back as a different bug a week later. The fixes that stuck were the ones I understood.

The loop that worked was: Claude proposes, I question, we refine, I write, we review. Nine journal entries captured that loop in real time. The journal became the blog.

the repo is open

github.com/tomerlevy1/bomberman-multiplayer.

It's not a tutorial. It's not polished. But it has all the commits in sequence, the ADRs, the journal entries, the specs, and the mentorship rules I wrote for Claude. If you want to see what "AI-assisted learning" actually looked like for one JS dev learning Rust, it's all there.

The sprites aren't. Those were Atomic Bomberman's and I can't share them. The placeholder is well-documented — bring your own tile sheets.

what's next

I don't know yet. The game is playable. It works on a LAN. Four players can connect and fight it out in the four corners, and there's no hard cap on connections. It has all the mechanics a round of Bomberman needs. I could add bots (there's a design note for FSM-driven virtual clients in the ADRs), add proper matchmaking, port the client to native, or start something else in Rust since I now have a language I enjoy.

The honest answer is "we'll see." I set out to learn Rust. I learned Rust.

Thanks for reading.