Every language promises something.

JavaScript promises that objects just work. Go promises simplicity. C promises control. C++ promises performance.

But there's one promise every language makes that none of them keep: "Your data won't change when you don't expect it to."

Rust is different. Rust keeps that promise. And the way it does it is what makes Rust feel scary at first — but also what makes it powerful.

The Promise Every Language Breaks

Think about what you want when you pass data to a function:

  1. You want to know if it can be changed. (Can this function mutate my data?)
  2. You want to know if your data is safe. (Can this function cause a memory leak?)

Every language fails at #1. Let me prove it.

JavaScript Can't Promise Safety

In JavaScript, there's no way to know if a function will change your object:

function process(obj) {
  obj.value += 1;  // Does this mutate? Who knows!
}

You'd need to read the function body. And even if you do, the function might change later. Or call another function that changes things. Or use Object.freeze() which is easily bypassed and doesn't work recursively.

Go Can't Promise Safety

Go has the same problem. Pass a struct to a function and you get a copy — unless you pass a pointer. But here's the catch: there's no way to know from the signature whether a function will mutate your data.

func process(o O) {  // Copies o - safe?
func process(o *O) {  // Modifies original - dangerous?

You can't tell from the function signature. You have to read the implementation. And even then, does the implementation call other functions? Do those mutate? You can't know.

C and C++ Can't Promise Anything

In C/C++, you can mark something const:

void pinky_promise_i_wont_mutate_u(const S& s) {
    S* s2 = const_cast<S*>(&s);  // HAHA JOKE'S ON YOU
    s2->i += 1;  // Mutated anyway!
}

const_cast just... removes const. There's no enforcement. The compiler can't stop you.

What Rust Actually Delivers

Rust makes promises it can keep. Here's the same code in Rust:

fn show(s: String) {
    println!("{}", s);
}

fn main() {
    let s = String::from("hiya");
    show(s);      // s is moved here
    show(s);      // ERROR: s was already moved
}

That second line is a compile error. Not a runtime error. A compile error. The compiler is telling you: "You already gave away ownership of s. You can't use it again."

This isn't a limitation. This is the feature.

The Promise of Ownership

When you pass a String to a function in Rust, you have two options:

  1. Move it — give ownership to the function. The function is now responsible for cleaning it up.
  2. Borrow it — let the function use it without taking ownership.

There's no hidden third option. No "maybe it copies, maybe it doesn't." No "I'll freeze this object" that doesn't really work. The signature tells you everything:

fn process_owned(s: String) { /* takes ownership */ }
fn process_borrowed(s: &String) { /* just looks at it */ }

That's it. That's the promise.

The Promise of Immutability

Here's where it gets beautiful. When you borrow something in Rust, it's automatically immutable:

struct Conn { name: String }

fn get_conn_name(c: &Conn) -> &str {
    &c.name  // Can't mutate - it's a shared reference
}

fn main() {
    let conn = Conn { name: String::from("prod-db") };
    println!("{}", get_conn_name(&conn));
    println!("{}", get_conn_name(&conn));  // Works fine - shared reference
}

Try to mutate through a shared reference:

fn get_conn_name(c: &Conn) -> &str {
    c.name = String::from("hacked!");  // ERROR: cannot assign to c.name
    &c.name
}

The compiler says: "No. It's a shared reference, so the data cannot be written."

This isn't a lint warning. This isn't a style suggestion. This is a compile error. You cannot mutate through a shared reference. Period.

Why This Matters

This isn't academic. Think about real code:

In other languages, you need to read every function's implementation to know these things. In Rust, you know from the signature.

fn close(conn: Conn) { /* takes ownership, will close */ }
fn get_name(conn: &Conn) -> &str { /* just reads */ }

The type system is telling you everything. That's the promise.

The History Behind the Promise

Rust didn't invent this. The concept is called linear types, and it's been around since 1990 when Philip Wadler wrote "Linear types can change the world!"

Languages like Clean, Haskell, and Pony have explored similar ideas. But Rust is the first mainstream language to make it practical.

The key insight: if you know exactly who owns a value, you know exactly when it's cleaned up. No garbage collector needed. No reference counting. Just compile-time certainty.

What This Means for You

When you're learning Rust, that "use of moved value" error feels annoying. Why can't I just pass this string twice?

But that error is Rust keeping its promise. It's saying: "You already gave this away. I won't let you use it again, because that would be a bug."

The scary part — ownership, borrowing, the borrow checker — is exactly what makes Rust work. It's not a hurdle to overcome. It's the feature.

The Promise

Every language promises:

Only Rust keeps that promise at compile time. The borrow checker isn't fighting you. It's protecting you.

The part that makes Rust scary is the part that makes it unique. And it's also what you'll miss in every other language once it clicks.


This post was inspired by Amos's "The Promise of Rust" at fasterthanli.me — go read the original for even more depth on how Rust compares to C, C++, Go, and JavaScript.