<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Recover on Roberto Selbach</title><link>https://rselbach.com/tags/recover/</link><description>Recent content in Recover on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Tue, 12 Apr 2011 19:54:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/recover/index.xml" rel="self" type="application/rss+xml"/><item><title>Panic Is Not an os.Error</title><link>https://rselbach.com/panic-is-not-an-os-error/</link><pubDate>Tue, 12 Apr 2011 19:54:00 +0000</pubDate><guid>https://rselbach.com/panic-is-not-an-os-error/</guid><description>&lt;p&gt;Go has both ordinary failure values and &lt;code&gt;panic&lt;/code&gt;, which guarantees that programmers will eventually use one where the other belongs. I started by treating panic like an exception mechanism for file failures. That made simple callers mysterious and was the wrong model.&lt;/p&gt;
&lt;p&gt;An operation expected to fail should return an &lt;code&gt;os.Error&lt;/code&gt; in this snapshot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;load&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;) ([]&lt;span style="color:#66d9ef"&gt;byte&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;f&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Open&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;O_RDONLY&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;f&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;readAll&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;f&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Missing files, refused connections, and malformed input are normal outcomes at a system boundary. The caller needs to decide what to do, so the value belongs in the function&amp;rsquo;s contract.&lt;/p&gt;
&lt;p&gt;Panic is better reserved for a broken invariant or a condition from which the current operation cannot sensibly continue. During panic, normal execution stops and deferred calls run while the stack unwinds. If nothing recovers, the program terminates with diagnostic information.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;recover&lt;/code&gt; can intercept that process, but only when called from a deferred function during unwinding. Calling it during ordinary execution does nothing useful. My worker boundary uses the pattern to prevent one internal invariant failure from taking down the entire test harness:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;runJob&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;job&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Job&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;chan&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Result&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;func&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; recover(); &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;failedResult&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;execute&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;job&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This boundary records the failure and marks that job bad. It does not silently resume halfway through &lt;code&gt;execute&lt;/code&gt;; its state may be inconsistent. Recovery belongs at a boundary that can abandon the failed unit of work.&lt;/p&gt;
&lt;p&gt;I avoid broad recovery in library functions. Converting every panic into &lt;code&gt;os.Error&lt;/code&gt; can hide programmer defects, and recovering without reporting the value destroys the most useful evidence. A panic due to an indexing mistake is not suddenly a network timeout because both traveled through one handler.&lt;/p&gt;
&lt;p&gt;These names and details reflect the April 2011 toolchain. Panic and recovery semantics have changed during Go&amp;rsquo;s development, so old examples need their snapshot attached. The design rule is stable enough for me: return ordinary failures, panic on violated assumptions sparingly, and recover only where an entire operation can be discarded cleanly. Anything broader starts to resemble sweeping broken glass under a very concurrent rug.&lt;/p&gt;</description></item></channel></rss>