{"id":826,"date":"2026-11-06T05:02:39","date_gmt":"2026-11-06T05:02:39","guid":{"rendered":"https:\/\/abrarqasim.com\/blog\/rust-borrow-checker-errors-read-them-before-you-clone\/"},"modified":"2026-11-06T05:02:39","modified_gmt":"2026-11-06T05:02:39","slug":"rust-borrow-checker-errors-read-them-before-you-clone","status":"publish","type":"post","link":"https:\/\/abrarqasim.com\/blog\/rust-borrow-checker-errors-read-them-before-you-clone\/","title":{"rendered":"Rust Borrow Checker Errors: Read Them Before You Clone"},"content":{"rendered":"<p>Short version for the impatient: most borrow checker errors are the compiler asking one question, &ldquo;who is still holding this?&rdquo;, and you can answer it faster than you think once you stop reading the error from the top.<\/p>\n<p>I came to Rust from PHP and JavaScript, where nobody asks who owns anything. You pass an array around, mutate it in three places, and find out on Friday which place was wrong. So the first few weeks of Rust felt like arguing with a very polite auditor. The error messages are good, genuinely better than most compilers manage. My problem was that I read them as complaints instead of as questions, and my fix for every complaint was the same: add <code>.clone()<\/code> until it went away.<\/p>\n<p>That works, and it&rsquo;s also how you end up with a Rust program that is slower than the PHP it replaced. Here are the four errors I see most, what each one is really asking, and the restructure that beats cloning.<\/p>\n<h2 id=\"the-rule-the-compiler-is-enforcing\">The rule the compiler is enforcing<\/h2>\n<p>The <a href=\"https:\/\/doc.rust-lang.org\/book\/ch04-02-references-and-borrowing.html\" rel=\"nofollow noopener\" target=\"_blank\">Rust Book chapter on references and borrowing<\/a> states it in two lines. At any given time you can have either one mutable reference or any number of immutable references to a value. And references must always be valid.<\/p>\n<p>That&rsquo;s the whole borrow checker. Everything else is the compiler working out, for each reference in your function, the stretch of code between where it&rsquo;s created and where it&rsquo;s last used. If a mutable and an immutable stretch overlap on the same value, you get an error. The word &ldquo;last used&rdquo; matters. Since non-lexical lifetimes landed, a borrow ends at its final use and not at the closing brace, which is why some code that looks guilty compiles fine.<\/p>\n<p>Hold on to that idea of a stretch of code. Every error below is two stretches overlapping.<\/p>\n<h2 id=\"moved-value-you-handed-it-away\">Moved value: you handed it away<\/h2>\n<p>The most common one for newcomers is <a href=\"https:\/\/doc.rust-lang.org\/error_codes\/E0382.html\" rel=\"nofollow noopener\" target=\"_blank\">E0382<\/a>, use of a moved value. The <a href=\"https:\/\/doc.rust-lang.org\/book\/ch04-01-what-is-ownership.html\" rel=\"nofollow noopener\" target=\"_blank\">ownership chapter<\/a> says each value has one owner, and passing a <code>Vec<\/code> or a <code>String<\/code> into a function by value transfers that ownership. The caller can&rsquo;t use it afterwards.<\/p>\n<pre><code class=\"language-rust\">fn shout_all(v: Vec&lt;String&gt;) -&gt; Vec&lt;String&gt; {\n    v.into_iter().map(|s| s.to_uppercase()).collect()\n}\n\nfn main() {\n    let names = vec![String::from(&quot;ada&quot;), String::from(&quot;linus&quot;)];\n    let loud = shout_all(names);\n    println!(&quot;{} names, first is {}&quot;, names.len(), loud[0]);\n    \/\/ error[E0382]: borrow of moved value: `names`\n}\n<\/code><\/pre>\n<p>My old reflex was <code>shout_all(names.clone())<\/code>. It compiles. It also copies every string in the vector just so a function can read them. The question the compiler is really asking is whether <code>shout_all<\/code> needs to own the data, and here it plainly doesn&rsquo;t. It only reads.<\/p>\n<pre><code class=\"language-rust\">fn shout_all(v: &amp;[String]) -&gt; Vec&lt;String&gt; {\n    v.iter().map(|s| s.to_uppercase()).collect()\n}\n\nfn main() {\n    let names = vec![String::from(&quot;ada&quot;), String::from(&quot;linus&quot;)];\n    let loud = shout_all(&amp;names);\n    println!(&quot;{} names, first is {}&quot;, names.len(), loud[0]);\n}\n<\/code><\/pre>\n<p>Taking <code>&amp;[String]<\/code> instead of <code>Vec&lt;String&gt;<\/code> also makes the function accept arrays and slices, so it&rsquo;s the better signature regardless. If a function only reads, borrow. If it keeps the value or changes it for good, take ownership. Cloning is the third option, and it should be a decision rather than a reflex.<\/p>\n<h2 id=\"mutating-while-someone-is-reading\">Mutating while someone is reading<\/h2>\n<p>Next up is <a href=\"https:\/\/doc.rust-lang.org\/error_codes\/E0502.html\" rel=\"nofollow noopener\" target=\"_blank\">E0502<\/a>: you cannot borrow something as mutable because it&rsquo;s already borrowed as immutable. The classic version is holding a reference into a vector and then pushing to it.<\/p>\n<pre><code class=\"language-rust\">fn main() {\n    let mut scores = vec![10, 20, 30];\n    let top = &amp;scores[0];\n    scores.push(40);\n    println!(&quot;top score: {top}&quot;);\n    \/\/ error[E0502]: cannot borrow `scores` as mutable\n    \/\/ because it is also borrowed as immutable\n}\n<\/code><\/pre>\n<p>Coming from other languages this looks harmless, and in Rust the danger is concrete. <code>push<\/code> may reallocate the vector&rsquo;s buffer, and then <code>top<\/code> points at memory that has been freed. In C++ that&rsquo;s a bug you find in production. Here it&rsquo;s a compile error, which I now consider a fair trade even on the days it irritates me.<\/p>\n<p>The two stretches in this example are easy to see: <code>top<\/code> is alive from line 3 to line 5, and <code>push<\/code> sits in the middle. There are two honest fixes, and the right one depends on what you wanted. If you only needed the value, copy it out, since integers are <code>Copy<\/code>:<\/p>\n<pre><code class=\"language-rust\">let top = scores[0];\nscores.push(40);\nprintln!(&quot;top score: {top}&quot;);\n<\/code><\/pre>\n<p>If you needed the reference, finish using it before you mutate, which means moving the <code>println!<\/code> above the <code>push<\/code>. What you should not do is reach for <code>RefCell<\/code> or <code>unsafe<\/code> to silence the check. <code>RefCell<\/code> moves the same conflict from compile time to a runtime panic, which is a worse place to find it.<\/p>\n<h2 id=\"two-mutable-borrows-through-a-method\">Two mutable borrows through a method<\/h2>\n<p><a href=\"https:\/\/doc.rust-lang.org\/error_codes\/E0499.html\" rel=\"nofollow noopener\" target=\"_blank\">E0499<\/a> is the one that taught me the most, because the fix isn&rsquo;t obvious until you know one detail about how the checker sees structs. Take a struct with two separate collections and a helper method for each.<\/p>\n<pre><code class=\"language-rust\">struct Inventory {\n    items: Vec&lt;String&gt;,\n    log: Vec&lt;String&gt;,\n}\n\nimpl Inventory {\n    fn items_mut(&amp;mut self) -&gt; &amp;mut Vec&lt;String&gt; {\n        &amp;mut self.items\n    }\n    fn note(&amp;mut self, msg: &amp;str) {\n        self.log.push(msg.to_string());\n    }\n}\n\nfn main() {\n    let mut inv = Inventory { items: vec![], log: vec![] };\n    let items = inv.items_mut();\n    inv.note(&quot;adding a lamp&quot;);\n    items.push(&quot;lamp&quot;.to_string());\n    \/\/ error[E0499]: cannot borrow `inv` as mutable more than once\n}\n<\/code><\/pre>\n<p>Look at what the signature of <code>items_mut<\/code> promises: a mutable borrow of <code>self<\/code>, the whole struct, for as long as the returned reference lives. The checker doesn&rsquo;t peek inside the method body to see that only <code>items<\/code> is touched. So <code>note<\/code> collides with it, even though <code>log<\/code> and <code>items<\/code> have nothing to do with each other.<\/p>\n<p>Inside a single function, though, the checker does track fields separately. That gives you the fix:<\/p>\n<pre><code class=\"language-rust\">fn main() {\n    let mut inv = Inventory { items: vec![], log: vec![] };\n    let items = &amp;mut inv.items;\n    inv.log.push(&quot;adding a lamp&quot;.to_string());\n    items.push(&quot;lamp&quot;.to_string());\n}\n<\/code><\/pre>\n<p>Same behaviour, no clone, no <code>RefCell<\/code>. The cost is that you&rsquo;re reaching into fields from outside the struct, which some people dislike on design grounds. If that bothers you, the other fix is to split the struct into two smaller ones that each own one collection, and I think that&rsquo;s usually where the design wanted to go anyway. If you want another example of ownership details shaping an API, I wrote up <a href=\"https:\/\/abrarqasim.com\/blog\/rust-1-99-vec-into-parts-and-the-box-leak-habit-i-had-to-drop\" rel=\"noopener\">Vec::into_parts and the Box leak habit I had to drop<\/a>. In my experience an awkward API is often pointing at an awkward ownership story.<\/p>\n<h2 id=\"missing-lifetime-specifier\">Missing lifetime specifier<\/h2>\n<p>The last one is <a href=\"https:\/\/doc.rust-lang.org\/error_codes\/E0106.html\" rel=\"nofollow noopener\" target=\"_blank\">E0106<\/a>, and it has the scariest reputation. It shows up when a function returns a reference and the compiler can&rsquo;t tell which input the reference comes from.<\/p>\n<pre><code class=\"language-rust\">fn longest(a: &amp;str, b: &amp;str) -&gt; &amp;str {\n    if a.len() &gt; b.len() { a } else { b }\n}\n\/\/ error[E0106]: missing lifetime specifier\n<\/code><\/pre>\n<p>The function could return either argument, so the caller needs to know how long the result stays valid. The fix is a small annotation that says both inputs and the output share one lifetime:<\/p>\n<pre><code class=\"language-rust\">fn longest&lt;'a&gt;(a: &amp;'a str, b: &amp;'a str) -&gt; &amp;'a str {\n    if a.len() &gt; b.len() { a } else { b }\n}\n<\/code><\/pre>\n<p>The <code>'a<\/code> doesn&rsquo;t change how long anything lives. It&rsquo;s a label that tells the checker &ldquo;the result is valid for as long as both of these are&rdquo;. The <a href=\"https:\/\/doc.rust-lang.org\/book\/ch10-03-lifetime-syntax.html\" rel=\"nofollow noopener\" target=\"_blank\">lifetime syntax chapter<\/a> walks through exactly this example, and I&rsquo;d read it twice. I did not understand it the first time, and I&rsquo;m still not sure I&rsquo;d explain it well to a colleague without a whiteboard.<\/p>\n<p>One more thing I&rsquo;ll say, since people ask: if you find yourself writing lifetime annotations on a struct in your second week, stop and return an owned <code>String<\/code> instead. Lifetimes in structs are a real tool. They are also a lot of ceremony for code that wasn&rsquo;t hot to begin with.<\/p>\n<h2 id=\"how-i-read-the-errors-now\">How I read the errors now<\/h2>\n<p>Here&rsquo;s the routine I follow when the compiler complains, and it took me embarrassingly long to settle on it. I skip the top line and go to the labelled spans in the middle of the message. Rust marks where the first borrow starts, where the conflicting use happens, and where the first borrow is still needed. Those three marks are your two overlapping stretches. Then I ask which one I can shorten.<\/p>\n<p>Usually I can shorten the first by finishing with it earlier, or copy a small value out instead of holding a reference, or change a function signature from owned to borrowed. Only after that do I consider a clone, and when I do, I leave a comment saying why. I also wrote about <a href=\"https:\/\/abrarqasim.com\/blog\/rust-vs-cpp-who-checks-the-lifetime-moved-pointer-segfault\" rel=\"noopener\">who checks the lifetime in Rust versus C++<\/a>, and the short reason to put up with all this is that the check exists so you aren&rsquo;t doing it by eye at 2am.<\/p>\n<p>I&rsquo;m still learning the harder territory here, namely async code holding references across <code>.await<\/code> points, and trait objects with borrowed data. Those errors look different and I don&rsquo;t trust my instincts on them yet.<\/p>\n<h2 id=\"try-this-week\">Try this week<\/h2>\n<p>Open one Rust project of yours, or a small throwaway, and run <code>grep -rn \"\\.clone()\" src\/<\/code>. For each hit, ask whether the function receiving it only reads. If it does, change the parameter to a borrow and delete the clone. Do five of them and run <code>cargo build<\/code> after each, so the compiler tells you which ones were hiding a real ownership question. If you want help moving a PHP or Node service toward Rust without a rewrite, my <a href=\"https:\/\/abrarqasim.com\" rel=\"noopener\">portfolio<\/a> shows the kind of migration work I take on.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The four Rust borrow checker errors I hit most, what each one is really asking, and the restructure that beats .clone(). Before and after code for every case.<\/p>\n","protected":false},"author":2,"featured_media":825,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"","rank_math_description":"The four Rust borrow checker errors I hit most, what each one is really asking, and the restructure that beats .clone(). Before and after code for every case.","rank_math_focus_keyword":"rust borrow checker","rank_math_canonical_url":"","rank_math_robots":"","footnotes":""},"categories":[142],"tags":[145,630,146,50,64],"class_list":["post-826","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-rust","tag-borrow-checker","tag-lifetimes","tag-ownership","tag-programming","tag-rust"],"_links":{"self":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/826","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/comments?post=826"}],"version-history":[{"count":0,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/826\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media\/825"}],"wp:attachment":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media?parent=826"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/categories?post=826"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/tags?post=826"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}