Rust encourages a rather different "high-level" programming style that doesn't suit the domains where C excels. Pattern matching, traits, annotations, generics, and functional idioms all sound great on paper, but when you're building low-level systems, they create an environment where everything becomes verbose, ceremony-driven, and semantically dense. You write more code about the code than about the actual work being done.
This is a consequence of Rust's "killer" feature: prevent entire classes of bugs at compile time. An ambitious goal, and when it works, it's magic. But the price is that the language must express more information than the actual algorithm demands. That extra information becomes noise in domains that already prize simplicity – bare-metal firmware, packet parsers, high-performance I/O loops. These are ecosystems where adding structure just because the type system encourages you to is not a virtue; it's overhead.
C, by contrast, is a different kind of animal. The language rewards terseness and economy of expression. You write exactly what you mean, and almost nothing else. Once you understand how pointers, lifetimes, and data layouts behave, it's like the compiler stops being a gatekeeper and becomes a quiet workhorse. It translates what you wrote into something very close to what the machine executes. No lifetimes to annotate. No type-level gymnastics to appease the borrow checker. No compiler front-end that feels like an argumentative partner.
The philosophical split between C-derived languages and Rust is deeper than "safe vs unsafe". It's about the role of the programmer in approaching a problem. Rust's mental model is rich, layered, and session-typed. It demands you to think in terms of ownership semantics, region constraints, and type traits. Even a simple data structure can grow a halo of metadata: derives, macros, lifetimes, Send/Sync bounds, feature gates. And while all of this scales well for large teams building complex asynchronous systems, it’s overkill in tight, performance-critical loops where the ideal number of moving parts is as few as possible.
C's mental model is brutally direct: bytes go here, pointers point there, and you control the boundaries. Unsafe? Yes. But also minimal. The clarity it provides in low-level domains isn't a side effect; it's the design goal. The language does not enforce guard rails, and therefore it does not require you to encode a novel's worth of constraints in your types. Good C codebases look simple not because they lack abstractions, but because they avoid abstractions that don't directly express the computation. And that's the thing Rust struggles with.
None of this means Rust is "bad" or that C is "better". It means they optimise for different values. Rust optimises for correctness and maintainability under heavy abstraction. C optimises for transparency and minimalism under extreme constraints.
Sometimes you don't want a language that keeps you safe. Sometimes you want one that simply gets out of your way.