How Learning Rust Ruined Other Languages for Me

Alain Delon

Every developer warns you about the borrow checker. They tell you about the steep learning curve, the hours spent wrestling with lifetimes, and the sheer frustration of a compiler that refuses to build code you know should technically run.

What they don’t tell you is what happens after the concepts finally click: Rust ruins other languages for you.


1. Option<T> and the Absence of Null

Tony Hoare famously referred to null references as his “billion-dollar mistake.” In Python, TypeScript, or Go, you are constantly checking if something is None, null, or nil. If you miss one check, your code blows up at runtime in production:

# Looks innocent until user is None
email = user.profile.get_email()

In Rust, null values simply don’t exist. If an operation can fail or produce an empty result, the compiler forces you to represent it as an Option<T>:

fn find_user(id: u64) -> Option<User> {
    // ...
}

match find_user(42) {
    Some(user) => println!("Found: {}", user.name),
    None => println!("User not found"),
}

You physically cannot forget to handle the empty state. If you try to use user without unwrapping or matching it, the compiler halts the build.


2. Error Handling as a First-Class Citizen

In languages that rely on try/catch exceptions, any function call is an invisible landmine. Does parse_config() throw a FileNotFoundError? A PermissionDeniedError? You often have to read the source code or trust out-of-date documentation to find out.

Rust uses Result<T, E>. A function’s signature declares its failure modes explicitly:

fn read_port(path: &Path) -> Result<u16, ConfigError>

With the ? operator, bubbling up errors becomes clean without masking what could actually go wrong:

let config_str = fs::read_to_string(path)?;
let port: u16 = config_str.trim().parse()?;

3. Fearless Concurrency

Data races in concurrent code are notoriously brutal to debug. They happen sporadically under load, resist reproduction in test environments, and rarely leave a clear stack trace.

In Rust, shared mutable state across threads is forbidden at compile time:

  • If data needs to cross threads, it must implement Send.
  • If data needs to be referenced across threads simultaneously, it must implement Sync.
  • You cannot mutate data shared between threads without a thread-safe wrapper like Mutex<T> or RwLock<T>.

The moment you attempt a data race, the compiler stops you.


The Takeaway

Writing Rust can feel slower upfront because you spend your time satisfying the compiler rather than writing logic. But once a binary compiles, it rarely breaks.

Returning to languages where functions return invisible nulls, throw untyped exceptions, and allow unchecked mutation suddenly feels like walking a tightrope without a net.