Wrong by Default
Tags: programminglanguages
Published: 2022-05-13 19:39:46

---
title: Wrong by Default
published: true
date: 2022-05-13 15:20:00 UTC
tags: ProgrammingLanguages
canonical_url: https://kevincox.ca/2022/05/13/wrong-by-default/
---

<main style="--width:min(36em,100%); display:grid; grid-template-columns:1fr var(--width) 1fr; overflow:auto; -moz-tab-size:4; -o-tab-size:4; tab-size:4; padding:0"><p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">A few programming languages use a “defer” pattern for resource cleanup. This is a construct such as a <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">defer</code> keyword which schedules cleanup code to run at the end of the enclosing block (or function). This is available in Zig, Go and even in GCC C.</p>
<div style="margin:0; box-sizing:border-box; padding:0; margin:0 1rem .8em; grid-column:2; grid-column:1/4; margin-left:0; margin-right:0; display:grid; grid-template-columns:1fr var(--width) 1fr; background:#222; border-top:1px solid #444; border-bottom:1px solid #444; overflow-x:auto"><pre style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2; grid-column:2; margin:.4em 1rem"><code style="margin:0; box-sizing:border-box; white-space:pre; background:none !important; color:#fff"><span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">fn</span> <span style="margin:0; box-sizing:border-box; padding:0">printFile</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(</span><span style="margin:0; box-sizing:border-box; padding:0">path</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">:</span> <span style="margin:0; box-sizing:border-box; padding:0">str</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">)</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">!</span><span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">void</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#999">{</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">var</span> <span style="margin:0; box-sizing:border-box; padding:0">f</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">=</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">try</span> <span style="margin:0; box-sizing:border-box; padding:0">open</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(</span><span style="margin:0; box-sizing:border-box; padding:0">path</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">)</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">defer</span> <span style="margin:0; box-sizing:border-box; padding:0">f</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">.</span><span style="margin:0; box-sizing:border-box; padding:0">close</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">()</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">for</span> <span style="margin:0; box-sizing:border-box; padding:0">line</span> <span style="margin:0; box-sizing:border-box; padding:0">in</span> <span style="margin:0; box-sizing:border-box; padding:0">f</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#999">{</span>
		<span style="margin:0; box-sizing:border-box; padding:0">print</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(</span><span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">try</span> <span style="margin:0; box-sizing:border-box; padding:0">line</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">)</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#999">}</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#ccc; font-style:italic; white-space:pre-wrap">// File is closed here.</span>
<span style="margin:0; box-sizing:border-box; padding:0; color:#999">}</span>
</code></pre></div>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">This provides a few key benefits.</p>
<ol style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">
<li style="margin:0; box-sizing:border-box">The cleanup is right next to the allocation. It is easy to spot and keep in sync.</li>
<li style="margin:0; box-sizing:border-box">The cleanup automatically occurs even for early returns and error cases.</li>
<li style="margin:0; box-sizing:border-box">The cleanup automatically occurs in the right order (reverse order of the keywords).</li>
</ol>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">This is a huge improvement to manually writing and running cleanup code. Each point above is a common source of bugs that are now easy to avoid.</p>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">However, <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">defer</code> has one huge downside. You need to remember it.</p>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">Compare to something like C++ or Rust:</p>
<div style="margin:0; box-sizing:border-box; padding:0; margin:0 1rem .8em; grid-column:2; grid-column:1/4; margin-left:0; margin-right:0; display:grid; grid-template-columns:1fr var(--width) 1fr; background:#222; border-top:1px solid #444; border-bottom:1px solid #444; overflow-x:auto"><pre style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2; grid-column:2; margin:.4em 1rem"><code style="margin:0; box-sizing:border-box; white-space:pre; background:none !important; color:#fff"><span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">fn</span> <span style="margin:0; box-sizing:border-box; padding:0">printFile</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(</span><span style="margin:0; box-sizing:border-box; padding:0">path</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">:</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">&amp;</span><span style="margin:0; box-sizing:border-box; padding:0">str</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">)</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">-&gt;</span> <span style="margin:0; box-sizing:border-box; padding:0">std</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">io</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">Result</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">&lt;</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">()</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">&gt;</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#999">{</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">let</span> <span style="margin:0; box-sizing:border-box; padding:0">f</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">=</span> <span style="margin:0; box-sizing:border-box; padding:0">std</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">fs</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">File</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">open</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(</span><span style="margin:0; box-sizing:border-box; padding:0">path</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">)</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">?</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">;</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">for</span> <span style="margin:0; box-sizing:border-box; padding:0">line</span> <span style="margin:0; box-sizing:border-box; padding:0">in</span> <span style="margin:0; box-sizing:border-box; padding:0">std</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">io</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">BufReader</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">::</span><span style="margin:0; box-sizing:border-box; padding:0">new</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(</span><span style="margin:0; box-sizing:border-box; padding:0">f</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">)</span><span style="margin:0; box-sizing:border-box; padding:0">.lines</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">()</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#999">{</span>
		<span style="margin:0; box-sizing:border-box; padding:0">println!</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(</span><span style="margin:0; box-sizing:border-box; padding:0; color:#b3d733">"{}"</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">,</span> <span style="margin:0; box-sizing:border-box; padding:0">line</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">?</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">);</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#999">}</span>
	<span style="margin:0; box-sizing:border-box; padding:0">Ok</span><span style="margin:0; box-sizing:border-box; padding:0; color:#999">(())</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#ccc; font-style:italic; white-space:pre-wrap">// File is closed here.</span>
<span style="margin:0; box-sizing:border-box; padding:0; color:#999">}</span>
</code></pre></div>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">Sure, adding one line of code isn’t hard. But you can forget. How do you even know what types need to be <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">close</code>d, <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">unlock</code>ed or otherwise disposed of? Automatic disposal (known as <a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization" style="margin:0; box-sizing:border-box; text-decoration:underline; -webkit-text-decoration-color:grey; text-decoration-color:grey">RAII</a>) takes this off the programmers mind and avoids bugs. Everything that needs to be cleaned up is cleaned up when it is no longer used.</p>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">Java has the same problem. Many programmers love garbage collectors because they clean up after you. But the problem with most garbage collectors is that they only clean up memory! There are many other types of resources such as locks, files, threads and sockets that you need to handle yourself. Some of these can be handled by finalizers but due to the possibly long length of time before they run they are not suitable for some resources such as locks.</p>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">A good resource management solution such as RAII can handle all resources that your program uses. For the longest time the best solution for java was <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">finally</code> blocks.</p>
<div style="margin:0; box-sizing:border-box; padding:0; margin:0 1rem .8em; grid-column:2; grid-column:1/4; margin-left:0; margin-right:0; display:grid; grid-template-columns:1fr var(--width) 1fr; background:#222; border-top:1px solid #444; border-bottom:1px solid #444; overflow-x:auto"><pre style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2; grid-column:2; margin:.4em 1rem"><code style="margin:0; box-sizing:border-box; white-space:pre; background:none !important; color:#fff"><span style="margin:0; box-sizing:border-box; padding:0">FileInputStream</span> <span style="margin:0; box-sizing:border-box; padding:0">inputStream</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">=</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">new</span> <span style="margin:0; box-sizing:border-box; padding:0">FileInputStream</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">(</span><span style="margin:0; box-sizing:border-box; padding:0; color:#b3d733">"story.txt"</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">);</span>
<span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">try</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">{</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#ccc; font-style:italic; white-space:pre-wrap">// Read file here...</span>
<span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">}</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">finally</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">{</span>
	<span style="margin:0; box-sizing:border-box; padding:0">inputStream</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">.</span><span style="margin:0; box-sizing:border-box; padding:0; color:#ffb433">close</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">();</span>
<span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">}</span>
</code></pre></div>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">While this is clearly a lot more boilerplate than defer and results in an unpleasant rightward shift, it is effectively the same. And most importantly you still need to remember which types need to be closed.</p>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">This has gotten slightly better in Java 7 with <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html" style="margin:0; box-sizing:border-box; text-decoration:underline; -webkit-text-decoration-color:grey; text-decoration-color:grey">the try-with-resources statement</a>.</p>
<div style="margin:0; box-sizing:border-box; padding:0; margin:0 1rem .8em; grid-column:2; grid-column:1/4; margin-left:0; margin-right:0; display:grid; grid-template-columns:1fr var(--width) 1fr; background:#222; border-top:1px solid #444; border-bottom:1px solid #444; overflow-x:auto"><pre style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2; grid-column:2; margin:.4em 1rem"><code style="margin:0; box-sizing:border-box; white-space:pre; background:none !important; color:#fff"><span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">try</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">(</span><span style="margin:0; box-sizing:border-box; padding:0">FileInputStream</span> <span style="margin:0; box-sizing:border-box; padding:0">inputStream</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">=</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#8585e0; font-weight:700">new</span> <span style="margin:0; box-sizing:border-box; padding:0">FileInputStream</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">(</span><span style="margin:0; box-sizing:border-box; padding:0; color:#b3d733">"foo.txt"</span><span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">))</span> <span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">{</span>
	<span style="margin:0; box-sizing:border-box; padding:0; color:#ccc; font-style:italic; white-space:pre-wrap">// Read file here...</span>
<span style="margin:0; box-sizing:border-box; padding:0; color:#f5a3a3; font-weight:700">}</span>
</code></pre></div>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">But even still, manual action is required for correctness. However, the best part of this feature may be the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/AutoCloseable.html" style="margin:0; box-sizing:border-box; text-decoration:underline; -webkit-text-decoration-color:grey; text-decoration-color:grey"><code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">java.lang.AutoCloseable</code> interface</a>. Finally there is a way to know which types need to be cleaned up. This machine-visible information may allow effective lints to be built. If you ever get an <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">AutoClosable</code> type you better use it in a try-with-resources statement or quickly pass it to someone else (and shift the burden to them). But complex cases would be difficult to reliably detect. For example if you store the type in a data structure and clean it up after it is removed. Even simply passing references to multiple places creates ambiguity on who is responsible for cleaning it up.</p>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">The morale of the story is that the best way to avoid bugs is to make them difficult to write. Ideally make bugs harder to write than correct code. <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">defer</code> and try-with-resources are “wrong by default” and require explicit work on behalf of the programmer to become correct. RAII is correct without the programmer doing anything extra.</p>
<p style="margin:0; box-sizing:border-box; margin:0 1rem .8em; grid-column:2">This post was inspired by the <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">defer</code> keyword, but it’s a much more general problem than that. I remember a document in Google about the <code style="margin:0; box-sizing:border-box; white-space:pre; padding:.1em; border-radius:.3em; background:#000; border:1px solid #555; color:#fff">--dont-be-stupid</code> flag. These commonly get added when a bug is found, but the fix contains an behavioural change that some client depends on, or the change needs to be carefully rolled out. After a few years most services will have dozens of these flags, all hardcoded on in the service configuration. Of course every once in a while a new instance of the service will be set up and this bug will reappear. Backwards compatibility and gradual rollouts are good, but at some point you need to update the code to be right by default.</p></main>