AI Engineer Europe 2026
Why Rust is the Ideal Language for Vibe-Coding
Read the talk
Why Rust is the Ideal Language for Vibe-Coding
First-attempt runnable code is a weak target for agentic development. Rust’s compiler gives an agent enforceable constraints and specific feedback for its next revision.
From a talk by Daniel Szoke
Before you start: Basic familiarity with type checking, async code and shared mutable state is helpful; no prior Rust experience is required.
Which language should a coding agent use?
Which language should you choose for agentic coding? Python, JavaScript and TypeScript are familiar answers. Daniel Szoke, who introduces himself as Sentry’s Rust SDK maintainer, begins with that conventional choice. When Szoke asked ChatGPT, it recommended Python first and JavaScript/TypeScript second. His own experience put TypeScript ahead.
The GitHub report he cites ranked TypeScript first by distinct monthly code contributors in August 2025. That measures participation, not language quality or coding-agent performance. GitHub’s suggested explanation—that AI-assisted development helped drive the change—is a suspected influence, not established causation. The question is what makes these languages attractive to agents in the first place.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Why familiar languages produce quick first drafts
The advantages are practical, and most help human programmers as well as agents:
- Familiarity: These are common languages, often learned early, with conventions that both people and models recognize.
- Existing building blocks: Frameworks, libraries and examples let a new application start from established components.
- Fast iteration: Python and JavaScript make it easy to run code and inspect its behavior. TypeScript adds compilation to JavaScript, but still supports a quick edit-and-run cycle.
- Typing support: Type annotations can catch misuse before execution, although TypeScript’s
anyand Python’sAnyweaken those checks where they are used.
Together, familiar patterns and relatively few constraints make it easier for a model to produce runnable code on its first attempt. That is an appealing starting experience: generate something, run it, observe the result and iterate.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Runnable code is not the same as reliable code
Ease of generation and reliable behavior are different objectives. A language that readily accepts a model’s first draft also gives that draft room to contain mistakes. The same flexibility that makes JavaScript, Python and TypeScript convenient can admit both obvious errors and subtle ones. Adding types helps, but type checking alone does not address every failure class, and escape hatches reduce its coverage.
Szoke expects model errors to persist even as models improve, pointing to their nondeterministic behavior. The engineering requirement is therefore familiar: just as software needs safeguards against mistakes by capable human programmers, agent-generated software needs safeguards against model mistakes. Better generation does not remove that requirement.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
What tests and review can establish
Tests and code review provide substantial protection, but relying on them alone leaves several distinct gaps.
| Safeguard | Remaining limitation |
|---|---|
| Tests written after implementation | An agent may reproduce implementation details in its tests instead of checking the intended behavior independently. |
| Test-driven development | Writing tests first does not make exhaustive coverage of input combinations practical. |
| Agent-written tests and agent review | The model can make mistakes in the verification work too. |
A failing test exposes an incorrect result for the case it exercises. Passing a collection of cases generally cannot prove that every possible input produces the right output. Tests remain useful evidence; they are not a universal proof of correctness.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Plausible code can fail in unfamiliar ways
Yuval Noah Harari’s Nexus gives Szoke another way to frame the problem. The book follows information networks from early human societies through the printing press and internet to AI. As Szoke recounts it, Harari prefers “alien intelligence” because “artificial” understates how differently these systems operate from human minds.
For coding, the implication is that a model’s mistakes may not resemble the ones a human reviewer expects. Token prediction can produce sensible variable names, clear comments and convincing structure while concealing a subtle bug. It can also produce an unnecessary heuristic where a direct check would be both simpler and more reliable. The appearance of careful programming is not evidence that the underlying decision is sound.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Add a deterministic checking layer
Szoke invokes Murphy’s Law to motivate another layer of protection: if a failure remains possible, repeated opportunities may eventually expose it. Human review, agent review and testing can reduce that risk. A deterministic check can go further for the particular rules it enforces, rejecting a violation every time it encounters one.
Rust is built around many such constraints. It is a compiled language designed for safety and performance, aiming at C and C++’s performance class while enforcing rules around types, memory and concurrent access. Successful compilation increases confidence that covered classes of errors are absent; it does not establish that the application implements the right behavior.
Editor’s note: Rust’s safety guarantees depend on safe Rust and correctly implemented unsafe abstractions. Unsafe operations carry obligations that programmers must uphold; unsafe does not disable every compiler check.
The compiler’s explanations matter as much as its refusal to accept a program. Rust aims to make diagnostics useful to learners by explaining what went wrong and often how to repair it. Those explanations also give a coding agent concrete context: compile, inspect the rejected operation and revise the code around the identified constraint.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
The constraints an agent must satisfy
Strict type safety removes the TypeScript-style any escape hatch from ordinary type checking. An agent cannot resolve a mismatch simply by declaring that the value should be exempt from those checks.
Explicit absence replaces a universal null value with Option<T>. A possibly missing value is represented as Some(value) or None, so accessing its payload requires working with that optional representation.
Editor’s note: This does not guarantee graceful handling of missing values. unwrap() and expect() are safe Rust methods that compile but panic when called on None.
Fearless concurrency extends checking to data used across threads. The compiler constrains which values can move between threads and which can be shared, preventing unsafe combinations from being accepted in safe code. These are only some of Rust’s enforced properties, but concurrency provides a particularly concrete example of how a constraint becomes useful feedback for an agent.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
A shared counter that the compiler rejects
The example begins with a counter initialized to zero and concurrent work that increments it. After all 100 increments complete, the intended counter value is 100. The problem is the choice of shared state: the types support shared mutation within one thread, but do not provide synchronization for access across threads.
In an implementation that permits unsynchronized access to shared memory, a counter could intermittently finish with the wrong total. Inside a larger application, locating that race could be difficult. Szoke uses TypeScript as the contrast, then points out that Rust rejects his example before execution.
Editor’s note: The JavaScript analogy requires an appropriate shared-memory setup, such as workers accessing a SharedArrayBuffer. Ordinary async tasks on one JavaScript thread do not by themselves create this cross-thread data race.
The diagnostic concerns an async future that cannot safely be sent between threads. Rust expresses that requirement through Send: a type implementing it permits ownership to transfer between threads. Although Szoke describes the concurrent work as threads, the diagnostic does not imply a separate operating-system thread for every increment.
Further down, the compiler identifies the captured value: Rc<RefCell<i32>> is not Send. Rc supplies shared ownership within a thread, while RefCell permits mutation through runtime borrow checking; neither combination supplies the cross-thread synchronization this counter needs. The central constraint can be isolated without choosing an async runtime:
rust
use std::{cell::RefCell, future::Future, rc::Rc};
fn increment(counter: Rc<RefCell<i32>>) -> impl Future<Output = ()> + Send {
async move {
*counter.borrow_mut() += 1;
}
}
The return type requires a Send future, but that future captures an Rc. This is the mismatch the agent needs to repair, rather than a vague instruction to make the program safer.
For the same counter, a thread-safe replacement is Arc<Mutex<i32>>: Arc provides shared ownership across threads, and the mutex protects the read-modify-write operation. Expressing that repair in the increment function gives:
rust
use std::{future::Future, sync::{Arc, Mutex}};
fn increment(counter: Arc<Mutex<i32>>) -> impl Future<Output = ()> + Send {
async move {
let mut value = counter.lock().expect("counter lock poisoned");
*value += 1;
}
}
The diagnostic has narrowed the agent’s next decision to a concrete one: choose a shared-state representation suitable for the concurrency the program requires.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Evaluate the repair loop, not just the first draft
More constraints make Rust harder for a model to get right on its first attempt. That cost looks different when the model operates inside an agent that can use tools and revise its work:
- Generate or modify the Rust code.
- Compile it.
- Inspect failures and their explanations.
- Repair the identified problems and compile again.
A compiler error is actionable feedback, not merely a failed generation. Each error potentially prevents a production bug. The qualification matters: static checks can conservatively reject valid programs too, so compiler errors are not a one-for-one count of bugs avoided.
Szoke argues that compilation is faster than asking an AI agent to review the code. He presents that as a practical comparison, without a timing benchmark. The more fundamental distinction is what each check establishes: the compiler enforces its covered rules, while an agent reviewer may overlook their violation. Review still has a place, including for behavior beyond the compiler’s guarantees. The proposed workflow keeps it and adds a deterministic layer that the agent can use on every revision.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
The sponsored closing
Szoke closes by explicitly identifying the talk as sponsored and returning to his Sentry affiliation. The closing promotion offered attendees three free months of Sentry’s Business plan through a QR code. He also mentions agent monitoring features and invites attendees to the downstairs booth to ask about Sentry or discuss the talk.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Yuval Noah Harari's history of information networks, from early societies to artificial intelligence.
Official Rust SDK and integrations for reporting application events, errors and panics to Sentry.
Further reading
- Understanding Send and SyncDocumentation
Explains Rust's rules for transferring ownership and sharing references across threads, including Rc, RefCell, Arc and Mutex.
Reference and examples for optional values, payload extraction, fallback values and panic behavior.
A practical tutorial on async tasks, ownership and Send bounds, with examples of compiler rejection involving Rc.
Read the transcript and explore slides
Loading transcript and slides…