DHH put an old language argument back in circulation at Rails World in September. He talked about agents building Rust code for HEY while being candid about how little he enjoys reading Rust. A couple of days later, he pointed out that C++ also works as a target for prompt-driven coding.
That exchange interested me because it separates two things I care about: the language that teaches me the most about a computer, and the language that gives an agent the strongest feedback while it writes code. My answers are C++ and Rust, respectively.
The reaction quickly became a referendum on Rust syntax, Ruby’s future, and whether humans need to read code at all. I find the compiler question more useful. An agent absorbs much of the cost of writing unfamiliar syntax; the compiler still decides how much bad ownership code gets through the first gate. DHH’s later C++ comment is a reminder that both languages can be generated. They give different feedback after generation.
C++ gives me an enormous design space. I can choose object representation, allocation strategy, ownership convention, ABI boundary, compile-time abstraction, and the exact point where a resource begins and ends. That openness has made it one of my best teachers. Rust asks me to express more of the lifetime and sharing story in types the compiler can check. I still control allocation and layout, but safe Rust will refuse a design whose ownership story it cannot verify.
C++ has been the deeper workshop for me. Rust is my default for a new systems component when agents help write the code.
Control Is a Set of Responsibilities
“Control over memory” covers several jobs: selecting an allocator, fitting a structure to a wire format, managing lifetime, deciding when copies occur, and handing memory to a foreign library. C++ lets me choose the convention for each job. Rust can work at the same level, including through unsafe, but its safe subset makes ownership and sharing rules visible in types.
The difference I feel is in the default contract. C++ lets me form many ownership arrangements and asks discipline, review, and tooling to keep them coherent. Rust makes ownership and borrowing central to ordinary compilation. When I hand a reference to another component, the compiler has a strong opinion about whether the referenced value will still exist and who may mutate it.
C++ has excellent tools for resource discipline: RAII, value types, smart pointers, and a narrow use of raw ownership. The C++ Core Guidelines explain why they matter. Rust puts a more uniform ownership vocabulary into ordinary compilation, as the Rust book shows.
Why C++ Teaches Me More
In C++, I cannot rely on a single ownership vocabulary across every library and era of the language. A pointer might be borrowed, owning, nullable, array-backed, or tied to a custom allocator. A callback might outlive the object that registered it. An exception might unwind through a resource boundary I did not think about. Even a seemingly harmless copy can become a serious cost when it moves a large buffer.
To work well in that environment, I have to inspect the exact lifetime and representation choices. I have to understand what is generated, allocated, copied, destroyed, and exposed across an ABI. That effort develops systems intuition. I have learned more from tracing one incorrect lifetime than from memorizing a long list of language features.
That work develops systems intuition. It also consumes time: every implicit convention is one more thing a reviewer has to reconstruct. I value the lesson C++ gives me and the opportunity Rust gives me to spend that review time elsewhere.
Why Rust Gains Value With Agents
An agent can generate a plausible implementation faster than I can inspect every line. That makes compiler feedback more valuable. If generated code retains a borrowed value too long, shares mutable state in an invalid way, or omits a case from an exhaustive match, Rust forces the issue into the development loop. The agent revises the code before handing me a build.
Marc Brooker’s feedback-loop argument helps explain why this matters to agents: fast, precise feedback makes a task easier to iterate on. Rust supplies that feedback for ownership and several concurrency errors. I can spend more of my own review time on behaviour, interfaces, and failure semantics.
flowchart TB
intent[Engineering intent] --> agent[Agent edits Rust]
agent --> compiler[Rust compiler]
compiler -- ownership error --> agent
compiler -- builds --> tests[Behaviour tests]
tests -- failure --> agent
tests -- pass --> review[Human checks system rules]
The compiler shortens one feedback loop. Tests add another. I still have to decide whether the implementation preserves the system rule those loops are meant to protect.
At the FFI boundary, unsafe puts the proof obligation back on the programmer. Native lifetime, pointer validity, and error behaviour need written contracts; the Rust Reference explains that obligation. I discuss the human side of those contracts in Agentic Engineering Without Surrendering Understanding.
One System, Different Language Jobs
FERS made this distinction concrete for me. A C++23 library owns the simulation world, radar objects, execution, and receiver output. It exposes a small C interface. A Rust/Tauri bridge owns the opaque native handle on the desktop side, and the React client sees validated state rather than native pointers.
The division is useful. C++ owns the mature simulation model and its low-level lifetimes. C provides the interface. Rust owns the foreign edge and application-side lifetime of the handle. Language choice follows the work each part must do.
Where I Use Each
The FERS core belongs in C++. Its domain knowledge and tested behaviour are worth far more than the appeal of a rewrite. For a new isolated systems service, parser, or worker built with substantial agent assistance, I choose Rust by default. The compiler makes iteration more exact, and review starts from stronger ownership guarantees.
I still want engineers using Rust to learn C and C++. The compiler can prevent an invalid borrow without teaching why the underlying lifetime mattered. Mastery means being able to explain the machine-level contract even when the language enforces part of it for you.
C++ keeps teaching me how the machine works. Rust lets me turn much of that knowledge into constraints the next piece of code has to satisfy. I want both: the mastery to recognize a broken design and the compiler feedback to catch a broken implementation early.