I enjoy writing Rust, but delegating some of its hardest mechanics has changed how I think about programming, understanding, and maintaining software.

My experience with AI-written code has been different
A complaint I encounter often about coding agents is that they produce strange, opaque, or brittle code, so the safest habit is to route every serious task through the strongest model available. I take that complaint seriously. People who have watched an agent invent a clever abstraction in the wrong layer, or leave a change nobody can resume next week, are describing a real cost.
My own Rust projects have usually gone differently. The recurring pattern looks ordinary from the outside. I ask an agent for a bounded change: extend a CLI flag, repair an async ownership error, add a test around a parsing edge case. The agent iterates against the compiler and the suite. Later, sometimes in a different tool and sometimes with a lighter model, I ask a fresh agent to continue.
Across Codex, Cursor, Pi, and OpenCode, and across both frontier models and lighter worker models, the result has generally been code I could read, question, and hand off without rebuilding the whole mental model from a chat transcript. Sometimes a lighter model finished the task without a heavier model coordinating it. When that worked, the next session continued from the repository: the types, the tests, the README, and the compiler output carried enough of the prior decisions that the new agent could proceed without the old conversation.
I like reading and writing Rust. I use it for programming puzzles and smaller personal projects, and I come to it as a functional-programming enthusiast who prefers explicit data flow, compositional design, constrained mutation, and type-oriented modeling. I am still developing as a Rust engineer rather than claiming industrial depth. Working with agents is partly an experiment in locating the boundary between tasks I am comfortable delegating and decisions I need to understand or control. I want to learn what makes AI-assisted code trustworthy enough that I am willing to keep evolving it. Whether agents are reliable in every setting is a different question from the one I am trying to answer here.
That experience pushed me to look past model rankings alone. If different tools and model classes can produce code I can still own, then language, architecture, checks, and review habits may be carrying more of the load than the brand of the last model that typed a line.
I still enjoy writing Rust, but I don’t need to solve every puzzle
My attraction to Rust predates coding agents. Ownership, algebraic data types, pattern matching, and explicit errors already matched how I like to think about programs. Writing Rust can feel like a puzzle I enjoy for its own sake, the way some people enjoy Sudoku or a Rubik’s Cube. The constraints are sharp, the feedback is immediate, and a solution that fits is satisfying.
That pleasure has a cost on real projects. I have often spent more attention translating a fairly settled idea into ownership, lifetime, and async patterns the compiler will accept than deciding what the program should do. The design choice was already clear by mid-morning. The afternoon went into making the expression legal: which value owns the buffer, whether a reference can outlive a scope, how an async task is allowed to touch shared configuration.
There is an asymmetry here that matters for delegation. For ordinary Rust in the style I tend to write, I often find reading and judging the result easier than composing the same code from scratch. That asymmetry can break with dense macros, elaborate generic machinery, or unsafe blocks. Inside the range where it holds, an agent can absorb much of the iterative search for a compile-acceptable expression while I keep the work of evaluating whether the expression is the right one.
I still enjoy solving those puzzles myself. Delegation leaves the craft intact; it changes how often a given project should spend my attention on the translation layer first. Pleasure in programming and the attention allocation appropriate to a particular piece of work are different questions, and AI has made it easier to separate them.
A compiler fix can also be an architectural decision
The boundary between expression and design is permeable. Consider an illustrative case rather than a claim about one specific repository incident.
Suppose an async task needs configuration or intermediate data whose ownership does not line up cleanly with the task’s lifetime. An agent facing a borrow-checker or lifetime error has several candidate moves. It might clone the data into the task. It might move ownership and restructure callers. It might wrap the value in Arc and share it. It might introduce interior mutability or locking if mutation is involved. Some of those options may be invalid for the exact problem. Others may compile while changing what the program means under load, under failure, or under later extension.
The candidates are not interchangeable. Cloning can be fine for small, infrequently copied values and expensive or semantically wrong when identity or freshness matters. Moving ownership can clarify a single-owner design and force awkward API changes elsewhere. Shared ownership via Arc can make concurrent access possible; it can also hide contention, extend lifetimes in ways that are hard to audit, or paper over a design that wanted a clearer owner. Synchronization can restore safety after shared mutation appears, and it can also change latency and failure modes.
The compiler is doing real work in this scene. It rejects programs that violate ownership and borrowing rules. It does not choose the best tradeoff among ownership, concurrency, performance, and maintainability. A compiling fix can still be more complex than necessary, alter behavior, or encode a design I would not have chosen if I had slowed down.
That is why I still want programming literacy even when I happily hand the trial-and-error loop to an agent. I want to recognize which candidate fix landed, ask whether it matches the design I care about, and request a revision when the tradeoff looks wrong. Code literacy lets me keep authority over consequential choices after implementation becomes cheaper. I am describing the control I want over software I am responsible for, rather than writing a curriculum rule for every possible use of AI.
This is also where “trust” starts to split into more than one question. Delegation trust asks whether I am willing to give a bounded implementation task to an agent. Artifact trust asks whether I can understand the result and gather enough evidence that it behaves as intended. Maintenance trust asks whether someone or something else can evolve it later without recovering the original agent’s hidden reasoning. A green build helps with part of artifact trust and leaves the rest open.
I like when the code tells me what it permits
Rust helps that review when important decisions leave a visible residue in the program. A small tabular-data CLI is a useful illustration. Suppose the tool must handle missing values under one of three policies: reject the row as an error, drop the row, or impute a numeric value. Representing those policies as an enum makes the domain distinction part of the type system:
|
|
This snippet is illustrative, not production-ready input validation or a complete statistical policy. Still, the shape carries several decisions at once. Result separates failure from success. Option separates a retained value from an omitted row. The enum names the allowed policies instead of leaving them as informal instructions in a chat prompt. If I later add a fourth policy and leave this exhaustive match unchanged, compilation fails until the handler is revisited. A new agent inherits that pressure without needing the original conversation. The prior design decision survived in a form the next implementer can inspect and a tool can check.
When an agent proposes such a change, my review is correspondingly narrow. I check that the new variant names a real domain choice, that every match site was updated for a reason, and that the tests describe the behavior I meant rather than whatever first compiled. The type system makes the unfinished work visible. It does not tell me whether the policy itself is the right scientific or product decision.
cargo check and the compiler’s diagnostics then give an agent a concrete edit target. Tests cover behavior the type system cannot know: whether imputation is scientifically appropriate for the analysis, whether upstream parsers feed the policy correctly, whether the CLI’s user-facing messages match the intended workflow. Safe Rust rules out important classes of memory-safety errors. What compilation leaves open is still large: algorithmic correctness, statistical soundness, security, adequate performance, and good structure. unsafe, external systems, and runtime behavior each keep their own obligations.
A non-Rust reader already knows related patterns elsewhere. TypeScript discriminated unions, OCaml algebraic data types, and statically checked Python patterns can encode similar domain distinctions with different guarantees. The transferable habit is to put important decisions where the next implementer can see them and a tool can check them.
Functional-style code is easier for me and fresh agents to follow
Encoding contracts helps, but a program can still demand too much surrounding context to change safely. This is where my older preference for functional-style design meets agent handoffs.
I prefer mostly pure transformations, explicit parameters and return values, limited shared mutable state, composable functions, and side effects at recognizable boundaries. Picture a small pipeline that parses a table, applies a missing-value policy, and writes a summary. If each stage is a function from values to values, with I/O kept at the edges, a change to the imputation rule stays local. An agent can read the policy type, the one function that interprets it, and the nearby tests. If the same behavior is tangled through a long-lived mutable service object that caches rows, mutates counters, and writes logs mid-transformation, the agent has to reconstruct more ambient state before it can trust a patch. When a different model or coding tool resumes the work with incomplete context, that difference in locality shows up quickly.
Rust encourages parts of this style through immutability by default, enums, pattern matching, iterator APIs, explicit errors, and ownership rules. That encouragement stops well short of making Rust a pure functional language. Ownership is compatible with impurity; interior mutability and external effects remain part of real programs. The benefit I see partly reflects architecture I choose. The same local-reasoning habits can be practiced in other languages.
I also watch for FP cleverness that hurts the handoff. Excessive generics, point-free compression, deeply nested iterator chains, and abstract machinery can make code harder for me and for a fresh agent to follow. I would rather keep composition boring and legible than impress anyone with density. The practical aim is a module a new contributor can reason about with limited context, whether that contributor is another person or another model.
The tools outlive the agent
Those design choices become more useful when every agent can get the same external feedback. Cargo gives common project workflows. cargo check, cargo test, cargo clippy, and cargo fmt --check provide complementary signals: type and borrow errors, behavioral regressions, lint smells, and formatting drift. Compiler diagnostics are often specific enough for an agent to iterate toward a legal program. Tests and human review address the behavior and design questions diagnostics cannot settle.
A fresh agent can inspect repository code, project instructions, tests, documentation, and tool output rather than depend solely on prior conversation state. In practice that means I spend more effort keeping the repository honest: a short README for how to run the checks, tests that name the behaviors I care about, and types that make illegal states harder to express. Those artifacts are ordinary. Their value shows up when the next agent never sees the previous transcript.
That is a plausible explanation for why, in my projects, lighter models and cross-tool handoffs have often gone better than an “always use the frontier model” rule would predict. The repository and its checks carry constraints that a chat transcript will eventually lose. Continuity lives in durable artifacts more than in the memory of whichever session wrote the last patch.
Tooling cannot manufacture good requirements. A weaker model can still struggle with architecture, nonlocal reasoning, subtle algorithms, or a poorly scoped task. Worker models are useful in my workflow; that is different from saying they match frontier models overall. For suitably bounded work, I increasingly invest trust in a well-specified project and its repeatable checks, while still deciding case by case when a harder task needs stronger assistance.
For that arrangement to hold, I still have to be able to read the code those checks leave behind.
Reading code is still a skill I want to keep
Rust’s official materials have been unusually helpful to me in building that reading skill: The Rust Programming Language, Rust by Example, the Reference, the standard library docs, and the error explanations. Among learning resources I have used, they are some of the most useful I have encountered for getting from curiosity to competent review. Becoming a useful reader still takes deliberate practice, and reading remains only one part of verification. Concurrency, unsafe, security, and sophisticated algorithmic or statistical logic can outrun a casual pass over a diff. In those areas I either slow down, add tests that encode the property I care about, or ask for help before treating a skim as enough.
I enjoy learning to write Rust and have no interest in giving that up. Delegation changes how often I personally perform difficult translations. Knowing enough to read the result lets me spot hidden architectural choices, ask for revisions, and notice when a change exceeds my competence.
AI has changed which programming abilities I exercise most frequently. For anyone who wants informed authority over implementation, learning to program still earns its keep. Review capacity is itself a limit. If an agent produces more code than I can meaningfully inspect, I prefer smaller tasks, legible interfaces, tests, and deliberate attention to consequential changes over accepting a large diff because it compiles. That preference is a personal operating habit rather than a mandatory playbook.
Why I keep coming back to Rust
I keep returning to Rust because this combination fits how I like to work. Agents can take on much of the mechanical translation into compilable code. I remain responsible for noticing when that translation smuggles in a design choice. Explicit types and enums make some of those choices discoverable. Functional-style decomposition keeps more changes local. Compiler feedback, Clippy, tests, and Cargo give agents and humans a shared loop that outlives any one session. Readable structure and documentation help the next agent resume without recovering the previous transcript.
The Rust ecosystem and tooling are mature enough for the kinds of projects I am talking about here: personal tools, puzzles, and smaller systems where I control the architecture. That is a scoped judgment about fit, not a ranking of languages. Exploratory scientific analysis, an existing team ecosystem, a very simple service, or an integration-heavy product may fit Python, Go, TypeScript, or another language better. Maintainable code belongs to many traditions.
What I want from a codebase, in Rust or elsewhere, is that important decisions survive outside the original author or AI session in forms the next implementer can inspect and check. Model capability still matters. So do requirements, security, deployment, observability, and domain validation; I am staying inside the narrower question of code-level comprehension and handoff. Within that question, my experience has made me care more about the choices that preserve understanding when developers and models change.
I enjoy Rust. I still want to understand it. AI has made it easier to spend my attention where I think my judgment matters most. If you work in a different language, the useful question is similar: which decisions does your codebase keep visible and checkable after the agent that wrote the last patch is gone?