Rust feels like it was designed for my brain

June 13, 2025Software

I've spent ten years writing Python and JavaScript professionally. I've built FinTech products, led engineering teams, and solved plenty of complex problems in both languages. I'm productive in them. I know their ecosystems. But after diving deep into Rust over the past year, I've realized something: I've been fighting against my natural way of thinking this entire time.


Rust doesn't just feel like a better tool. It feels like the language was designed around the same mental model I've always had about how programs should work.


Explicitness over magic


Python culture loves "magic." Metaclasses, descriptor protocols, `__getattr__` tricks, implicit type conversions, monkey-patching. The language rewards cleverness. You're supposed to appreciate the elegance of doing complex things in few lines. Dynamic typing means you can pass anything anywhere and hope it works out.


I've never liked this. Every time I encounter magic in a codebase, I have to stop and figure out what's actually happening. Where does this method come from? What type is this really? What happens if I pass the wrong thing here? The implicitness that Python developers celebrate feels like technical debt to me.


Rust is explicit about everything. Types are explicit. Ownership is explicit. Lifetimes are explicit. Trait bounds are explicit. When I read Rust code, I know exactly what's happening. There's no magic to uncover, no hidden behavior to discover at runtime. The compiler forces everything to be spelled out.


This matches how I think. I don't want to guess what a function does, I want the type signature to tell me. I don't want to wonder whether a mutation is safe, I want the borrow checker to prove it. Explicitness isn't verbosity; it's clarity.


The type system as a thinking tool


Here's what I've realized: strong static typing isn't a constraint on my thinking, it's an amplifier.


In Python, types are documentation at best, lies at worst. Type hints help, but they're optional and unenforced. You can write `def process(data: List[int]) -> str` and then return a dictionary anyway. The type system can't help you reason about your program because it's not actually part of the program.


JavaScript is even worse. TypeScript improves things, but you're still fighting the underlying dynamism. Any `any` can infect your codebase. Runtime and compile-time worlds don't fully align.


Rust's type system is something different entirely. It's not just checking types, it's encoding invariants, ownership semantics, and resource lifetimes directly in the type system. When I design a Rust API, I'm not just thinking about what functions to expose; I'm thinking about what states are representable, what transitions are possible, and what invariants the compiler can enforce.


This is how I naturally think about problems anyway. I think in terms of state machines, valid states, and impossible states. I think about what should be allowed to happen and what should be prevented at compile time. Python and JavaScript forced me to implement these invariants through runtime checks, documentation, and discipline. Rust lets me encode them in types.


The result is that I spend less time debugging and more time thinking. When my Rust code compiles, I have genuine confidence it's correct in ways I never had with Python or JavaScript.


Ownership and reasoning about resources


Memory management in Python is garbage collected. You allocate things and trust the GC to clean them up eventually. In practice, this works fine most of the time. But "most of the time" isn't good enough when you're thinking rigorously about what your program is doing.


Who owns this data? When will it be freed? Is this reference valid? Can this cause a memory leak? Python says "don't worry about it." JavaScript says the same. And look, I get it, for many applications, this is fine. GC is productive.


But I've never stopped thinking about these questions. Even in Python, I think about ownership semantics. Which parts of the code have responsibility for this data? Who's allowed to mutate it? What happens if multiple parts of the code hold references? I mentally track these things because that's how I understand program behavior.


Rust makes this explicit through ownership. Every value has a single owner. Borrowing is explicit and checked. Lifetimes are tracked. This isn't overhead for me, this is the language finally speaking the same language I've been thinking in all along.


And the benefits extend beyond memory. The ownership model applies to any resource: file handles, database connections, locks, anything. The type system enforces correct resource management. You can't forget to close a file or deadlock on a mutex because the ownership rules prevent it.


This is how I want to think about programs. Resources have lifecycles. Ownership has semantics. These things should be explicit and verified, not implicit and hoped-for.


Fearless refactoring


Here's something I didn't fully appreciate until I experienced it: Rust makes refactoring trivial in ways Python and JavaScript never could.


In Python, refactoring is terrifying. Change a function signature and hope you found all the call sites. Rename a field and pray nothing breaks at runtime. Refactor a class hierarchy and cross your fingers that no duck-typed code depends on methods you removed. Even with good test coverage, you're never quite sure.


JavaScript is similar. TypeScript helps, but the type system can be worked around. Tests help, but they're only as good as your coverage.


In Rust, refactoring is a conversation with the compiler. Change a type, and the compiler tells you every place that needs updating. Remove a field, and compilation fails at every access site. Modify an API, and every caller that breaks is immediately visible.


The compiler is exhaustive. If your code compiles after a refactor, you didn't miss anything. This isn't just convenient, it's transformative. It means I can make large structural changes with confidence. It means I can iterate on designs without fear of subtle breakage.


For someone who thinks carefully about architecture and wants to improve code continuously, this is incredible. The language actively supports evolving codebases instead of fighting against it.


Error handling that makes sense


Python has exceptions. JavaScript has exceptions. Both encourage a style where errors are thrown from deep in the call stack and caught somewhere higher up. In practice, this means:


- You're never sure what exceptions a function might throw

- Error handling is often forgotten until runtime

- Errors can bypass intermediate layers invisibly

- Control flow becomes non-local and hard to reason about


I've never liked this. Exceptions as control flow feel wrong. They break the explicit flow of the program. You can't look at a function signature and know what can go wrong.


Rust uses `Result<T, E>`. Errors are values. Functions that can fail say so in their return type. You can't ignore errors, the compiler forces you to handle them. Pattern matching makes error handling explicit and local.


This matches my mental model perfectly. Errors are part of a function's contract. They should be visible in the type signature. Handling them should be mandatory. The error path should be as explicit as the success path.


`Result` and the `?` operator make this ergonomic without sacrificing explicitness. I can see at a glance what operations might fail and how errors propagate. The control flow is explicit. The compiler ensures I don't forget edge cases.


Cargo and the ecosystem


Let's talk about tools. Python has pip, virtualenv, poetry, pipenv, conda, and a dozen other competing solutions for dependency management. JavaScript has npm, yarn, pnpm, and associated chaos. Both ecosystems suffer from fragmentation and complexity.


Cargo is what I wanted all along. One tool for building, testing, running, managing dependencies, and publishing. The convention over configuration philosophy means projects have consistent structure. Adding dependencies is trivial. Publishing crates is straightforward.


The crates.io ecosystem is remarkably high quality. Libraries generally have good documentation, follow conventions, and compose well. The culture emphasizes doing things properly rather than quickly.


This isn't just convenience, it's about removing friction from the development process. I can focus on solving problems instead of fighting build systems or dependency resolution.


It's not just the language


Here's the thing: Rust's technical features are important, but what really matters is the underlying philosophy. Rust respects the programmer's intelligence while refusing to trust their memory or attention to detail.


The language assumes you know what you're doing architecturally but recognizes that humans make mistakes. It catches those mistakes at compile time. It encodes good practices in the type system and ownership model. It makes correct code easier to write than incorrect code.


This matches how I think about programming. I'm capable of designing complex systems and reasoning about difficult problems. But I'm also human and will make mistakes. I want a language that catches my errors, enforces invariants I care about, and lets me encode my design intentions in ways the compiler can verify.


Python and JavaScript treated me like I needed to be protected from complexity through dynamism and flexibility. Rust treats me like an adult who wants powerful tools that prevent footguns.


The learning curve is worth it


Yes, Rust has a learning curve. The borrow checker takes time to understand. Lifetimes can be confusing initially. The error messages, while excellent, sometimes require thought to decipher.


But here's what I've found: the learning curve isn't learning arbitrary complexity. It's learning to think explicitly about things I was already thinking about implicitly. It's making my mental models concrete and verifiable.


Every concept that seemed hard initially (ownership, borrowing, lifetimes, trait bounds) turned out to be formalizing something I was already tracking mentally. Rust didn't teach me new ways to think about programs. It gave me a language for expressing how I already thought.


After ten years of Python and JavaScript, Rust feels like I can finally stop fighting the language and just solve problems. The compiler is a collaborator, not an obstacle. The type system amplifies my reasoning instead of constraining it. The ownership model makes explicit what I was already trying to enforce through discipline.


I'm not saying Rust is perfect for everyone or every use case. But for how my brain works (systematic, explicit, rigorous) it's exactly right. It feels less like learning a new language and more like finally being understood.