My Rusty Christmas: How Rust earned a place next to C++, Java, Python and Go (and made me argue with rust traits)

Crowds of fun-seekers exploring a city on foot, "

Arjan Franzen

6 January 2026

Pegboard met vijf gebruikte stukken gereedschap en een koperkleurige nieuwkomer met een eigen omtreklijn.

Every few years, a new language earns a permanent place in your toolkit.
Not because it’s fashionable.
Not because the internet says you should.

But because, at some point, you realise:

“This actually solves problems I already have — even if it annoys me first.”

This Christmas, Rust joined that list for me.

The starting point

My background is pretty broad:

  • ~10 years of C++ (systems, performance, RAII, undefined behaviour as lived experience)

  • A lot of Java (enterprise, scale, long-lived systems)

  • Plenty of Python (glue, speed of thought, experimentation)

  • Some Go (services, pragmatism — the least of the four)

Rust had been circling my radar for years. I admired it conceptually, but like many C++ developers, I had the usual thought:

“This looks nice, but I already know how to do this safely.”

So this Christmas, I stopped theorising and just started to actually build.

What I expected vs what happened

What I expected:

  • A steep learning curve

  • Many compiler errors

  • A lot of clever ideas wrapped in friction

What actually happened:

  • A steep learning curve

  • Many compiler errors

  • …and a growing suspicion that the compiler was usually right

A very early example:

It felt like a very opinionated code reviewer who had seen all my old mistakes before.

1let a = String::from("hello");    
2let b = a;    
3// println!("{a}"); // ❌ compile error
4

In C++, this would compile and later explode.

Rust just says “no” — immediately, and for a good reason.

Rust didn’t feel like a new paradigm.

Ownership and borrowing: fewer lies, more honesty

The big shift was ownership and borrowing.

At first it feels restrictive.

Then it doesn’t feel very pleasant.

Then — slowly — it feels honest.

1fn len(s: &String) -> usize {
2    s.len()
3}
4let s = String::from("hello");
5let n = len(&s);
6println!("{s}"); // ✅ still valid

No more casual aliasing.

No more “this reference should be fine”.

No more mental bookkeeping about who frees what and when.

Coming from C++, this didn’t feel like losing power — it felt like formalising discipline I already tried to apply manually.

Error handling without drama

Rust’s error model clicked faster than expected.

  • Errors are data

  • Failure is explicit

  • Control flow is visible

1fn read_config() -> Result<String, std::io::Error> {
2    let content = std::fs::read_to_string("config.toml")?;
3    Ok(content)
4}

The ? operator alone deserves a small shrine.

It reads exactly like what it does:

“If this fails, stop here — cleanly.”

After years of C++ exceptions and Go’s if err != nil, this felt like the grown-up version of both.

And then… numeric traits happened

At some point during Christmas, I made the classic mistake.

I wanted to write a simple function that “just works for numbers”.

Python brain said:

1def add(a, b):
2    return a + b

Rust replied:

1fn add<T>(a: T, b: T) -> T {
2    a + b
3}
4// ❌ nope

So I specified what Rust wanted:

1use std::ops::Add;
2fn add<T: Add<Output = T>>(a: T, b: T) -> T {
3    a + b
4}

Then I wanted to mix types.

1add(1i32, 2.0f64); // ❌ still nope
2

At some point, I found myself staring at trait bounds thinking:

“I have written template metaprogramming in C++.

Why is this harder than I expected?”

This is where that Rust numeric meme hits painfully close to home.

If you want “numbers” in Rust, you don’t get a free abstraction.

You get:

  • traits

  • bounds

  • associated types

  • crates like num

And the creeping realisation that this is deliberate.

Two muscular men facing each other with a Python logo and Rust logo, discussing programming concepts.

The uncomfortable realisation

Then it clicked.

Rust isn’t bad at numeric abstraction.

It’s refusing to lie.

Python says:

“I’ll figure it out at runtime.”

Rust says:

“Tell me exactly what you mean, or don’t ship.”

So you end up writing:

1fn add_as_f64<A: Into<f64>, B: Into<f64>>(a: A, b: B) -> f64 {
2    a.into() + b.into()
3}

Yes, that’s more explicit.

Yes, that’s more friction.

But it also means:

  • no silent truncation

  • no implicit widening

  • no platform-dependent behaviour

  • no “this worked in tests but not in production” math bugs

I didn’t enjoy that realisation in the moment.

But I respected it.

Undefined Behaviour (UB), now with fences

After enough years in C++, you don’t fear crashes.

You fear the bugs that only show up in production.

Rust’s stance on UB is refreshingly blunt:

1unsafe {
2    let p = 0xdeadbeef as *const i32;
3    println!("{}", *p); // UB lives here, clearly marked
4}

Yes, UB exists.

No, you don’t get it accidentally.

If you want it, you must opt in explicitly.

That changes how you think about boundaries — and about trust.

Async without a GC (still slightly magical)

Async Rust deserves its own article, but briefly:

1async fn fetch(url: &str) -> Result<String, reqwest::Error> {
2    let body = reqwest::get(url).await?.text().await?;
3    Ok(body)
4}
  • async/await without a garbage collector

  • Futures as explicit state machines

  • No runtime hiding memory costs

Yes, the borrow checker gets louder here.

Yes, you sometimes have to restructure code.

But the result is predictable performance with compile-time safety, which is still rare.

From Christmas experiment to actual work

The real signal wasn’t intellectual.

This didn’t stay a holiday experiment.

Before Christmas was over, I was already using Rust in a ZEN Software project — not as a rewrite for fun, but where Rust’s strengths actually fit:

  • correctness

  • performance

  • long-term maintainability

That’s usually the moment a language stops being interesting and starts being useful.

Where Rust sits in my toolbox now

I don’t think in terms of replacement.

For me, it now looks like this:

  1. Java→ stability, scale, predictability

  2. Python→ speed of thought, experimentation

  3. Rust → correctness under constraints, without giving up performance

  4. Go→ pragmatic services

  5. C++ → ultimate control, legacy ecosystems, sharp edges

Rust didn’t make me feel less experienced. It made me feel like my experience finally had a compiler that backed me up — even when it annoyed me.

Closing thoughts

I didn’t “switch” to Rust this Christmas.

I added it.

Yes, I argued with traits.

Yes, I cursed numeric bounds.

Yes, I lost a few hours to the borrow checker.

But the result stuck.

2026 is going to be a bit more Rusty.

And I’m genuinely looking forward to it. 🦀

Comic strip illustrating a humorous conversation among programmers, highlighting the appeal of Rust programming language.
no image placeholder

Optimize with ZEN's Expertise

How we can help

Custom software, built and run

Applications that follow your process, not the other way round. From first sketch to production, and we keep them running after that.

See custom software