Cole's Notes

A Simple Blog Powered by Go

Rust as Music, Part II: Harmony, Borrowing, and the Rules That Make It Beautiful

Posted by cole on Jan 20, 2026 12:59

In Part I, we learned how to hear Rust.
In Part II, we learn why it sounds the way it does.

If Rust were just strict, it would be unpleasant.
If it were only safe, it would be boring.

Rust is neither.

Rust is harmonically constrained, the way music is constrained — not to limit expression, but to make expression coherent.

The borrow checker is not a guardrail.
It is counterpoint.


Harmony Is Not Optional

In music, harmony is not decoration.
It is the rule-set that allows multiple voices to coexist without collapsing into noise.

Rust enforces harmony by answering one fundamental question at all times:

Who is allowed to touch this data, and how?

Everything else follows from that.


Borrowing as Musical Voice Allocation

Consider this:

let note = Note { pitch: 60, velocity: 100 };

let a = &note;
let b = &note;

This is harmony.

Multiple listeners may observe the same instrument.
No one interferes. No one retunes.

Now contrast:

let c = &mut note;

This is a solo.

Rust enforces a rule every ensemble understands instinctively:

You cannot have a soloist while the audience is on stage.

This is not a limitation — it’s what prevents chaos.


Why &mut Is Exclusive

The most misunderstood Rust rule is also the most musical:

  • Many immutable borrows or
  • One mutable borrow
  • Never both

Why?

Because mutation is not observation.
Mutation is performance.

If two performers try to retune the same instrument mid-song, the result is not creativity — it’s feedback.

Rust prevents this at compile time, before the concert begins.


Lifetimes Are Rhythm, Not Memory

Let’s reframe lifetimes properly.

fn echo<'a>(s: &'a str) -> &'a str

This does not mean:

“Here is a scary pointer with a label.”

It means:

“This phrase lasts at least as long as its source.”

Lifetimes describe relative duration, not location.

They are time signatures.

Rust does not ask where data lives —
It asks how long it is allowed to be referenced.


Elision: When the Music Is Obvious

Most Rust code doesn’t show lifetimes explicitly:

fn echo(s: &str) -> &str

This is lifetime elision — shorthand notation.

Just like musicians don’t annotate every rest when the rhythm is obvious, Rust allows you to omit lifetimes when the phrasing is unambiguous.

When you do need to write them out, it’s because the rhythm is complex — not because Rust is cruel.


Traits: Musical Theory, Not Inheritance

Traits are often explained as “interfaces,” but that undersells them.

A trait is a musical capability.

trait Playable {
    fn play(&self);
}

This does not say what something is.
It says what it can do.

Traits are not about lineage.
They are about role.

A violin and a flute are not related — but both are playable.

Rust embraces composition over inheritance because music does too.


impl Trait — Improvisational Space

fn perform(piece: impl Playable) {
    piece.play();
}

This reads as:

“Bring me anything that can perform.”

The concrete type doesn’t matter.
Only the capability does.

This is Rust allowing controlled improvisation — freedom within constraints.


Generic Constraints as Key Signatures

fn duet<T: Playable + Tunable>(a: T, b: T)

This is not verbosity.

This is the composer saying:

“This piece is written in a key that supports both harmony and tuning.”

Generic bounds are key signatures.
They prevent accidental dissonance.


Enums: Structured Variation

enum Sound {
    Note(Note),
    Rest,
    Chord(Vec<Note>),
}

An enum is not “a type with options.”

It is a closed musical form.

Rust forces you to handle every variant:

match sound {
    Sound::Note(n) => n.play(),
    Sound::Rest => {}
    Sound::Chord(c) => play_chord(c),
}

No missing cases.
No surprise silence.

The compiler ensures the composition is complete.


Pattern Matching: Reading the Score, Not Guessing

match is not a switch statement.

It is structural recognition.

Rust asks:

“Have you accounted for every possible musical phrase?”

If not, it refuses to perform.


Error Handling Is Part of the Composition

In Rust, errors are not side effects.

They are alternate endings.

fn load_track() -> Result<Track, Error>

The function does not promise success.

It promises honesty.

The ? operator is a conductor’s cue:

“If this fails, stop the piece and report upward.”

No hidden failures.
No swallowed notes.


Why Rust Feels Hard (Until It Doesn’t)

Rust feels difficult because it asks you to:

  • Think about time
  • Think about access
  • Think about responsibility

But once you internalize the music, you stop thinking consciously.

You hear when a borrow is wrong.
You feel when ownership must move.
You anticipate where lifetimes will clash.

This is not restriction.

This is mastery.


Rust Is Not Strict — It Is Honest

Rust does not prevent you from doing dangerous things.

It requires you to say them out loud.

Unsafe code is not forbidden — it is marked.

Just like dissonance in music, danger is allowed — but never silent.


Closing: Why This Model Endures

This way of thinking scales.

It works for:

  • game engines
  • ECS architectures
  • graphics pipelines
  • systems programming
  • concurrency

Anywhere harmony matters.

Rust does not teach you how to type code.

It teaches you how to listen.


Constraints are not the enemy of expression.
They are the reason expression survives.

Next: Part III: Rhythm, Concurrency, and Playing in Time (Async, Threads, and the Orchestra)


Read the score.
Hear the harmony.
Trust the music.

← Back to posts