The wall every Rust beginner hits
If you have learned Rust, you remember the first time the borrow checker stopped you. The code looked fine, you hit compile, and the screen filled with an error about moved values or lifetimes. Rust has some of the best compiler messages of any language, but they quietly assume you already understand ownership. For a beginner, that help does not land. It feels like a wall, and this is where a lot of people give up.
I wanted something that explains the error, not just the fix. So I built BorrowFix.
What it does
You pick one of 20 curated Rust compiler errors, or paste your own broken code. The site sends it to a small backend that runs rustc and returns the real compiler error. It only compiles the code and never runs it, which keeps things simple and safe.
The first version showed the error and a paragraph of explanation. It was accurate, and it read like a manual.
The feedback that changed the design
After I shared it on LinkedIn, Divyesh Kakadiya, a backend engineer who works in Rust and Go, tried it and left two suggestions. First, start with a simple analogy before the compiler error. Second, show the fixed code right next to the explanation, so a beginner can connect the error, the concept and the fix in one view.
Both were right. I rebuilt all 20 errors around that order: an analogy, the real compiler error, the Rust concept behind it, the fixed code, and a line on why the fix works.
A before and after
Take the classic moved value error.
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}", s1);
}
Before, the explanation said that assigning s1 to s2 moves ownership, so s1 is no longer valid. Now it starts like this: think of it like handing someone your only house key. Once you give it away, you cannot also use it yourself. Then comes the real error, then the concept (Strings own heap memory, and only one owner is allowed at a time), and then the fix:
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{}", s1);
println!("{}", s2);
}
The last line explains why it works: clone makes a full copy, so s1 and s2 each own separate data and both stay valid.
Here is another one, two mutable borrows at once.
fn main() {
let mut x = 5;
let r1 = &mut x;
let r2 = &mut x;
println!("{} {}", r1, r2);
}
The analogy is two people editing the same document at the same instant with no lock, where one person's changes silently vanish. Rust blocks that at compile time instead of letting it turn into a data race at runtime. The fix is to let r1 finish its work inside its own block before r2 starts a new borrow.
A technical detour worth sharing
I first planned to use Piston, the open source code execution engine, and self-host it on Railway. It failed. Piston relies on an isolation tool that needs privileged container access, and most hosting platforms, Railway included, do not allow that. The container started, but Piston crashed inside it with a read-only file system error.
Since I only needed compile checks for curated examples, I replaced it with a small Node and Express service that calls rustc directly with a timeout, a fresh temporary folder for every request, and no execution of the compiled output. The frontend is a static page on Vercel. It is a small setup, and it was enough.
What I would like from you
BorrowFix is early and rough in places. If you are learning Rust or teach it, tell me which compiler error confused you the most and what finally made it click. I will add it next.
You can try it here: https://borrowfix-smoky.vercel.app
Top comments (0)