From Gophers to Crustaceans

A light-hearted survival guide for Go developers who are ready to trade their gopher holes for crab shells. Adding Rust to Your Go Toolkit Hey there, fellow Gophers! You've likely spent considerable time appreciating Go's delightful simplicity, its straightforward concurrency model with goroutines and channels, and the sheer pragmatism that lets you build reliable and scalable services quickly. Go is fantastic, and it's not going anywhere. Go might ship fast, but Rust makes sure the ship doesn’t sink. - Panic! But maybe you've heard the buzz about Rust. Maybe you've seen it pop up in discussions about performance, systems programming, WebAssembly, or just admired its reputation for safety and reliability. Perhaps you're curious about what it offers and how it might fit alongside Go in your developer toolbox. Not a Replacement, an Addition Remember, the goal isn't to replace Go. It's to add Rust to your skillset. Think of it like adding a specialized, high-performance tool to your workshop. You wouldn't use a sledgehammer for finish carpentry, nor a delicate chisel for demolition. Go and Rust are both powerful tools, optimized for different (though sometimes overlapping) tasks. Knowing both makes you adaptable and lets you choose the best tool for the job. So, bring your Go knowledge, an open mind, and a willingness to wrestle with the borrow checker (just a little!). It's a rewarding journey. Learning Rust, from a Go background, feels like upgrading from automatic to manual transmission — initially stressful, ultimately empowering. You’ll stall the engine (borrow checker), but once muscle memory kicks in, you’ll wonder how you ever drove without a clutch. So sharpen your claws, embrace the uncompromising memory safety, and remember: If you can’t trust the garbage collector, become the garbage collector. – Probably Confucius Setting Expectations: The Learning Curve (Hello, Ownership!) Let's be upfront: Rust has a reputation for a steeper learning curve than Go, especially initially. Why? It largely boils down to how Rust achieves memory safety without a garbage collector: the Ownership and Borrowing system, often enforced with Lifetimes. In Go, the garbage collector tracks memory usage and cleans up objects that are no longer referenced. This is convenient but has runtime costs (CPU time, potential pauses) and doesn't prevent all concurrency issues like data races (though Go's race detector helps find them). Rust shifts this burden to the compiler. It enforces a strict set of rules at compile time about which part of your code "owns" a piece of data, who can borrow it (read-only or mutable), and for how long (lifetimes). Ownership: Each value in Rust has a single owner. When the owner goes out of scope, the value is dropped (memory is freed). Borrowing: You can lend out references (&) to data. You can have many immutable references or exactly one mutable reference (&mut), but not both simultaneously. This prevents data races. Lifetimes: These are (often implicit) annotations that tell the compiler how long references are valid, ensuring they don't outlive the data they point to (preventing dangling pointers). Learning these concepts and satisfying the compiler (affectionately known as "fighting the borrow checker") is the main hurdle for newcomers. But here's the good news: The compiler is your (strict but helpful) friend. Its error messages are often excellent and guide you toward the correct solution. Once you internalize these concepts, you start writing code that is inherently safer and often more performant. The pain is front-loaded. Once you get over the hump, development becomes much smoother. Background I'm Nicholas Ajwang'; currently, one of the rooks at the Zone 01 Kisumu's chess board. But I didn’t start here — I began as a pawn, just like anyone else who dares to make their first move. With every challenge, every project, and every line of code, I advanced one square at a time, guided by a community that believed in my potential. Zone 01 didn’t just teach me how to play the game — it taught me how to think strategically, to collaborate like a team, and to never underestimate the power of small, consistent progress. Today, I stand in a new position, empowered and ready to defend, support, and drive change on the chess board. If you’ve been waiting for your opening, this is it. The board is set, the pieces are moving — will you step up and play your part? Your transformation from pawn to powerhouse begins here: ♟️ Apply to Zone 01 Before Go Before joining Zone 01 Kisumu I had just enough C under my belt to be dangerous. This, I had learned the hard way, from the MIT of Nairobi, the Jommo Kenyatta University of Agriculture and Technology. At that time, solving differential equations in Calculus seemed so easier than doing a C exam; ooh, and it's not because we loved solving differential equations (even N

May 3, 2025 - 09:56
 0
From Gophers to Crustaceans

A light-hearted survival guide for Go developers who are ready to trade their gopher holes for crab shells.

Adding Rust to Your Go Toolkit

Hey there, fellow Gophers!

You've likely spent considerable time appreciating Go's delightful simplicity, its straightforward concurrency model with goroutines and channels, and the sheer pragmatism that lets you build reliable and scalable services quickly. Go is fantastic, and it's not going anywhere.

Go might ship fast, but Rust makes sure the ship doesn’t sink. - Panic!

But maybe you've heard the buzz about Rust. Maybe you've seen it pop up in discussions about performance, systems programming, WebAssembly, or just admired its reputation for safety and reliability. Perhaps you're curious about what it offers and how it might fit alongside Go in your developer toolbox.

Not a Replacement, an Addition

From-Gophers-to-Crustaceans-visual-selection

Remember, the goal isn't to replace Go. It's to add Rust to your skillset. Think of it like adding a specialized, high-performance tool to your workshop. You wouldn't use a sledgehammer for finish carpentry, nor a delicate chisel for demolition.

Go and Rust are both powerful tools, optimized for different (though sometimes overlapping) tasks. Knowing both makes you adaptable and lets you choose the best tool for the job.

So, bring your Go knowledge, an open mind, and a willingness to wrestle with the borrow checker (just a little!). It's a rewarding journey.

Learning Rust, from a Go background, feels like upgrading from automatic to manual transmission — initially stressful, ultimately empowering. You’ll stall the engine (borrow checker), but once muscle memory kicks in, you’ll wonder how you ever drove without a clutch.

So sharpen your claws, embrace the uncompromising memory safety, and remember:

If you can’t trust the garbage collector, become the garbage collector. – Probably Confucius

Setting Expectations: The Learning Curve (Hello, Ownership!)

Let's be upfront: Rust has a reputation for a steeper learning curve than Go, especially initially. Why? It largely boils down to how Rust achieves memory safety without a garbage collector: the Ownership and Borrowing system, often enforced with Lifetimes.

In Go, the garbage collector tracks memory usage and cleans up objects that are no longer referenced. This is convenient but has runtime costs (CPU time, potential pauses) and doesn't prevent all concurrency issues like data races (though Go's race detector helps find them).

Rust shifts this burden to the compiler. It enforces a strict set of rules at compile time about which part of your code "owns" a piece of data, who can borrow it (read-only or mutable), and for how long (lifetimes).

  • Ownership: Each value in Rust has a single owner. When the owner goes out of scope, the value is dropped (memory is freed).
  • Borrowing: You can lend out references (&) to data. You can have many immutable references or exactly one mutable reference (&mut), but not both simultaneously. This prevents data races.
  • Lifetimes: These are (often implicit) annotations that tell the compiler how long references are valid, ensuring they don't outlive the data they point to (preventing dangling pointers).

Learning these concepts and satisfying the compiler (affectionately known as "fighting the borrow checker") is the main hurdle for newcomers. But here's the good news:

  1. The compiler is your (strict but helpful) friend. Its error messages are often excellent and guide you toward the correct solution.
  2. Once you internalize these concepts, you start writing code that is inherently safer and often more performant.
  3. The pain is front-loaded. Once you get over the hump, development becomes much smoother.
Background

I'm Nicholas Ajwang'; currently, one of the rooks at the Zone 01 Kisumu's chess board. But I didn’t start here — I began as a pawn, just like anyone else who dares to make their first move. With every challenge, every project, and every line of code, I advanced one square at a time, guided by a community that believed in my potential.

Zone 01 didn’t just teach me how to play the game — it taught me how to think strategically, to collaborate like a team, and to never underestimate the power of small, consistent progress. Today, I stand in a new position, empowered and ready to defend, support, and drive change on the chess board.

If you’ve been waiting for your opening, this is it. The board is set, the pieces are moving — will you step up and play your part? Your transformation from pawn to powerhouse begins here: ♟️ Apply to Zone 01

Before Go

Before joining Zone 01 Kisumu I had just enough C under my belt to be dangerous. This, I had learned the hard way, from the MIT of Nairobi, the Jommo Kenyatta University of Agriculture and Technology. At that time, solving differential equations in Calculus seemed so easier than doing a C exam; ooh, and it's not because we loved solving differential equations (even Newton didn't).

We had to write the C code on paper! With a pen! Then, attempt to:

  • compile it
  • check for syntax errors, solve them
  • trace double free errors
  • use -> and NOT . where appropriate
  • fictionally run the code
  • get fictional results and compare them to the question's requirements

All of that, done in the brain, then submit the paper and hope it does not seg fault on the Lecturer, cause otherwise the results could not only be catastrophic to the C runtime, but also to your grade.

Syntax Warm-Up: Counting Crabs Before They Hatch

Let’s start with printing numbers.

Enter Go

When Go came along, it looked like C with most of the foot-guns safely confiscated:

  • Garbage-collector? ✔️
  • Concurrency? ✔️
  • No header files?
  • Double-✔️!

With all that level of detail in C, I wasn't just going to dump the hard-earned C logic and get along with Go. I carried my C logic to Go, and it worked perfectly, just with meager changes.

Some code snippets below will not run on their own, so you can copy them to the main function in the Go Playground or the Rust Playground and run to see the output.

Here's some C code to print from 0 to 10:

for(int i = 0; i < 10; i++) {
    printf("%d", i);
}

// Output: 0123456789

Now convert it to Go:

for i := 0; i < 10; i++ {
    fmt.Printf("%d", i)
}

// Output: 0123456789

Kaboom! The work is done.

We just need to forget about braces around for statements, and ignore trailing commas.

Going Rust

But then came Rust, promising C-levels of performance and memory safety without a GC. It sounded too good to be true. Spoiler: it’s true — but the onboarding is more RollerCoaster.rs than HelloWorld.go. Strap in.

Trying to copy the code to print from 0 to 10, as we did in C and Go above, will fail immediately.

Our fori loop (or iterative counter loop), does not naturally occur in Rust!! (even the unforgiving JavaScript, which notoriously has NOTHING to do with Java, was just too merciful to include).

So we need to rethink our solution.

Reading the rust reference, we get:

A for expression is a syntactic construct for looping over elements provided by an implementation of std::iter::IntoIterator.

So a for loop, is just a placeholder for looping over iterables; from a C perspective, it does not qualify to be called a for loop.

Aah! We need to create an iterable within our range, from 0 to 10, and load it into the for loop. Digging the reference, we can take advantage of the Range Expression to create our target iterable:

for i in 0..10 {
    print!("{}", i);
}

// Output: 0123456789

We created an iterator for the range 0 ≤ i < 10.
Then called the print macro function in the for loop, which also has a format specifier argument as our fmt.Printf function (Just that this Rust macro is smart and simply trades out Go's %d with a {}) to display the value of the current i to the standard output.

That’s all the shell-shaking we’ve got for this part of the series! But before we crab-walk to the next tide pool of Rust, here’s a fun little scuttle — can you flip the script and count down from 9 to 0 (inclusive), strictly by changing the 0..10 and nothing else? Go on, give those pincers a stretch.

Next Up: Let's get our hands dirty! In the next post, we'll cover setting up your Rust development environment and getting our number printer programs, side-by-side in Go and Rust, to run locally, comparing the build tools and basic project structure. Stay tuned!