<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: smudge</title>
    <description>The latest articles on DEV Community by smudge (@smudge).</description>
    <link>https://dev.to/smudge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F90382%2Fa344aea0-e457-4671-8547-0ecee7c68f88.png</url>
      <title>DEV Community: smudge</title>
      <link>https://dev.to/smudge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/smudge"/>
    <language>en</language>
    <item>
      <title>RubyConf 2021</title>
      <dc:creator>smudge</dc:creator>
      <pubDate>Tue, 16 Nov 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/smudge/rubyconf-2021-dhd</link>
      <guid>https://dev.to/smudge/rubyconf-2021-dhd</guid>
      <description>&lt;p&gt;Last week, despite being a Rubyist for 14 years, I attended my very first RubyConf!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyljgx7wlqezlaoetkk8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyljgx7wlqezlaoetkk8u.png" alt="RubyConf 2021 Banner" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trip to Denver was also a great opportunity to see my grandparents for the first time since the pandemic, and it also marked my return to in-person conferences.&lt;/p&gt;

&lt;p&gt;I took a bunch of &lt;a href="https://ngriffith.com/writing/rubyconf-2021/rubyconf2021-notes.pdf" rel="noopener noreferrer"&gt;notes&lt;/a&gt; and produced a giant Tweet thread about it that I'll attempt to unroll below (you may need to wait for twitter to load them all):&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1460815049350516737-761" src="https://platform.twitter.com/embed/Tweet.html?id=1460815049350516737"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815049350516737-761');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815049350516737&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815053637173253-442" src="https://platform.twitter.com/embed/Tweet.html?id=1460815053637173253"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815053637173253-442');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815053637173253&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815056376147970-64" src="https://platform.twitter.com/embed/Tweet.html?id=1460815056376147970"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815056376147970-64');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815056376147970&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815058921996289-247" src="https://platform.twitter.com/embed/Tweet.html?id=1460815058921996289"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815058921996289-247');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815058921996289&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815061333778436-407" src="https://platform.twitter.com/embed/Tweet.html?id=1460815061333778436"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815061333778436-407');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815061333778436&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815067495251977-63" src="https://platform.twitter.com/embed/Tweet.html?id=1460815067495251977"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815067495251977-63');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815067495251977&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815070930345986-243" src="https://platform.twitter.com/embed/Tweet.html?id=1460815070930345986"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815070930345986-243');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815070930345986&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815076542365699-372" src="https://platform.twitter.com/embed/Tweet.html?id=1460815076542365699"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815076542365699-372');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815076542365699&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815079377707010-876" src="https://platform.twitter.com/embed/Tweet.html?id=1460815079377707010"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815079377707010-876');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815079377707010&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815081680347137-630" src="https://platform.twitter.com/embed/Tweet.html?id=1460815081680347137"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815081680347137-630');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815081680347137&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815083647471616-911" src="https://platform.twitter.com/embed/Tweet.html?id=1460815083647471616"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815083647471616-911');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815083647471616&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815091734126595-458" src="https://platform.twitter.com/embed/Tweet.html?id=1460815091734126595"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815091734126595-458');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815091734126595&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815095718678534-299" src="https://platform.twitter.com/embed/Tweet.html?id=1460815095718678534"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815095718678534-299');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815095718678534&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815099120308224-309" src="https://platform.twitter.com/embed/Tweet.html?id=1460815099120308224"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815099120308224-309');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815099120308224&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815104224772096-804" src="https://platform.twitter.com/embed/Tweet.html?id=1460815104224772096"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815104224772096-804');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815104224772096&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815107215216640-512" src="https://platform.twitter.com/embed/Tweet.html?id=1460815107215216640"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815107215216640-512');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815107215216640&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815112646897671-283" src="https://platform.twitter.com/embed/Tweet.html?id=1460815112646897671"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815112646897671-283');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815112646897671&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815116040032260-606" src="https://platform.twitter.com/embed/Tweet.html?id=1460815116040032260"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815116040032260-606');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815116040032260&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815118296653826-118" src="https://platform.twitter.com/embed/Tweet.html?id=1460815118296653826"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815118296653826-118');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815118296653826&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815120712482816-95" src="https://platform.twitter.com/embed/Tweet.html?id=1460815120712482816"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815120712482816-95');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815120712482816&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815123191406599-198" src="https://platform.twitter.com/embed/Tweet.html?id=1460815123191406599"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815123191406599-198');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815123191406599&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815125418586115-64" src="https://platform.twitter.com/embed/Tweet.html?id=1460815125418586115"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815125418586115-64');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815125418586115&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815127687704580-793" src="https://platform.twitter.com/embed/Tweet.html?id=1460815127687704580"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815127687704580-793');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815127687704580&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815130061688834-613" src="https://platform.twitter.com/embed/Tweet.html?id=1460815130061688834"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815130061688834-613');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815130061688834&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460815363806015494-783" src="https://platform.twitter.com/embed/Tweet.html?id=1460815363806015494"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460815363806015494-783');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460815363806015494&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460816262553030659-242" src="https://platform.twitter.com/embed/Tweet.html?id=1460816262553030659"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460816262553030659-242');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460816262553030659&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1460816500802170882-114" src="https://platform.twitter.com/embed/Tweet.html?id=1460816500802170882"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1460816500802170882-114');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1460816500802170882&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>rubyconf</category>
      <category>rubyconf2021</category>
      <category>ruby</category>
    </item>
    <item>
      <title>All-In on Pattern Matching</title>
      <dc:creator>smudge</dc:creator>
      <pubDate>Tue, 05 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/smudge/all-in-on-pattern-matching-1n0m</link>
      <guid>https://dev.to/smudge/all-in-on-pattern-matching-1n0m</guid>
      <description>&lt;p&gt;I'd like to share a snippet of code I've been honing over the past few days.&lt;/p&gt;

&lt;p&gt;For context, I'm working on a command-line interface for taking photos from a webcam. I'm aiming to make it compatible as a drop-in replacement for the mac-based &lt;a href="https://github.com/rharder/imagesnap" rel="noopener noreferrer"&gt;imagesnap&lt;/a&gt; tool, and I'm writing it in Rust in the hopes of enabling cross-platform support. But for now, I'm focusing on a mac implementation and am (with a bit of magic) interfacing with one of macOS's Objective-C libraries (the &lt;a href="https://developer.apple.com/av-foundation/" rel="noopener noreferrer"&gt;AVFoundation&lt;/a&gt; framework).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But none of that matters to this post.&lt;/strong&gt; What matters is that I'm building a tool that will accept inputs from humans and translate those to a series of nontrivial behaviors under the hood. For anyone familiar with this kind of shell-command work, this means accepting and parsing a series of arguments that might be supplied in any number of combinations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;imagesnap &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; 2 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Microsoft LifeCam HD-3000"&lt;/span&gt; 2020-01-05-test-snapshot-1.jpg
Capturing image from device &lt;span class="s2"&gt;"Microsoft LifeCam HD-3000"&lt;/span&gt;..................2020-01-05-test-snapshot-1.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also means accepting zero arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;imagesnap
Capturing image from device &lt;span class="s2"&gt;"FaceTime HD Camera (Built-in)"&lt;/span&gt;..................snapshot.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, of course, it means gracefully handling invalid inputs as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;imagesnap &lt;span class="nt"&gt;-xzfv&lt;/span&gt;
Error: Unrecognized option: &lt;span class="s1"&gt;'x'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fact that this kind of human-machine interface pattern allows for practically limitless input permutations is what makes it so powerful and enduring. But it also presents an age-old challenge: we must design a &lt;code&gt;main&lt;/code&gt; method that can make sense of these inputs and conditionally adjust its behavior.&lt;/p&gt;

&lt;p&gt;And if there's one thing that I can say about conditionals, it's that the more of them you have, the more subtle the bugs are, and the harder it becomes to reason about edge cases.&lt;/p&gt;

&lt;p&gt;Let's take a look at what this commonly entails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional Argument-Handling In Practice
&lt;/h3&gt;

&lt;p&gt;In Rust, as in most languages, getting the list of user-supplied inputs is super easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the sake of conciseness, we can then use the &lt;code&gt;getopts&lt;/code&gt; library to define our program's options (and ensure that any unrecognized arguments result in an error state):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;getopts&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="nf"&gt;.optopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"warmup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Warm up camera for x seconds [0-10]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x.x"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="nf"&gt;.optflag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"l"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"List available capture devices"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="nf"&gt;.optopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"device"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Use specific capture device"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"NAME"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="nf"&gt;.optflag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"help"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"This help message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// might panic!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to quickly iterate on the argument-handling logic. If you're like me, your brain will naturally want to start introducing a series of procedural steps that walk through the inputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;list_devices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"l"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free.is_empty&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"snapshot.jpg"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;warmup_secs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="c1"&gt;// default based on rough manual testing&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;get_default_device&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a perfectly sensible approach and will look very similar across a lot of C-style languages, regardless of our use of &lt;code&gt;getopts&lt;/code&gt; (which simply helps us avoid explicitly looping through every input).&lt;/p&gt;

&lt;p&gt;You can start to picture where this might go—a bunch of variable assignments via a series of &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt;conditionals, all culminating in a final series of possible behaviors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print_usage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;list_devices&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;list_devices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;snap_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warmup_secs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following so far? Good.&lt;/p&gt;

&lt;p&gt;As of, say, Saturday, this is what my code looked like (more or less) and it worked well for prototyping. Of course, I could clean it up by handling return types (instead of blindly calling &lt;code&gt;unwrap()&lt;/code&gt; and hoping the runtime doesn't panic), but for simplicity's sake, I've excluded all of the error-handling logic from the above examples.&lt;/p&gt;

&lt;p&gt;And sure, there are bunch of ways I could've implemented this, but you'll probably agree that most of the above is relatively unobjectionable and, in all likelihood, uninteresting. So let's move onto the more interesting parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where It Breaks Down
&lt;/h3&gt;

&lt;p&gt;What the above approach doesn't help me with at all are the many corner cases that hide among the conditionals.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;getopts&lt;/code&gt; library will already return an &lt;code&gt;Err&lt;/code&gt; variant if any unrecognized arguments are passed, but say, for example, that I'd like to react to incompatible or nonsensical &lt;em&gt;combinations&lt;/em&gt; of otherwise _valid_arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;imagesnap&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Invalid&lt;/span&gt; &lt;span class="n"&gt;combination&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="nd"&gt;supplied!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make that work as a conditional, I'd need to first check if both were supplied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"l"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error: Invalid combination of arguments supplied!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But doing this across all of the inputs would be quite cumbersome! I might start chaining &lt;code&gt;opt_present&lt;/code&gt; calls together with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;/&lt;code&gt;||&lt;/code&gt; operators, or break out a series of conditionals to check for interesting permutations, but this expansion of conditional behavior is prone to error, and commonly results in &lt;code&gt;main&lt;/code&gt; methods becoming littered with guard clauses (often in response to user-reported bugs).&lt;/p&gt;

&lt;p&gt;Alternatively, I could choose to focus on stricter conditions around each behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"l"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;no_incompatible_options_present&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;list_devices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error: Invalid combination of arguments supplied!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yet, once again, solving for the "no incompatible options" condition will be annoying to reason about, and may require an update every time I add a new program argument.&lt;/p&gt;

&lt;p&gt;A common resolution to the above conundrum is, simply, to not solve it, and rely on the user to supply valid inputs, lest they encounter undefined behavior. Such tendencies are a result the high cost of getting it right, relative to the low cost of getting it wrong. &lt;em&gt;It's a minor irk, easily worked around, so why bother?&lt;/em&gt; I wouldn't fault anyone for making such a trade-off.&lt;/p&gt;

&lt;p&gt;But what if we could make it easier? What if there were a low-cost way of &lt;em&gt;visualizing&lt;/em&gt; the whole matrix of possible inputs, and expressing that visualization in code?&lt;/p&gt;

&lt;h3&gt;
  
  
  Patterns and Matching
&lt;/h3&gt;

&lt;p&gt;Yep, this is where pattern matching comes in.&lt;/p&gt;

&lt;p&gt;As a quick example of how Rust's match syntax works, let's look at that filename conditional from before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free.is_empty&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"snapshot.jpg"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could instead be expressed as a pattern match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"snapshot.jpg"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fans of functional languages will recognize this pattern. (My first experience with pattern matching was with Haskell.) But in this example, we're matching on just a single value, and even with the fancy &lt;code&gt;get(0)&lt;/code&gt; call (returning an &lt;code&gt;Option&lt;/code&gt;type), the overall approach is functionally equivalent to the &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Where pattern matching truly shines is when we can compose multiple values and types. Case in point: that "no incompatible options" problem from before. What if we solved that by matching for patterns of &lt;em&gt;multiple inputs&lt;/em&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"l"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;list_devices&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--list/-l is incompatible with other args!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;continue_execution&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're talking! Similar to before, we're relying on &lt;code&gt;opt_str&lt;/code&gt; to return an &lt;code&gt;Option&lt;/code&gt; type. In other languages, this might be a null-check, but in Rust, we can pattern-match on type variants and guarantee that we've covered all possible permutations of these three inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes, that's a guarantee.&lt;/strong&gt; Rust won't compile our code unless we've accounted for all possible &lt;code&gt;match&lt;/code&gt; branches. This encourages us to rely on wildcard/catch-all arms like that &lt;code&gt;continue_execution()&lt;/code&gt; arm above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Going All-In
&lt;/h3&gt;

&lt;p&gt;So why not go all the way, and include &lt;em&gt;all&lt;/em&gt; of our inputs in a single &lt;code&gt;match&lt;/code&gt;? Some might call it crazy, but I've found incredible utility in pattern-matching on inputs, early and often.&lt;/p&gt;

&lt;p&gt;Sure, it won't prevent logical bugs in the way we parse and handle args, but it will at least ensure that we express_all possible permutations_ of user input. This provide a much better starting place than the previous pile of loosely-related conditionals.&lt;/p&gt;

&lt;p&gt;Convinced? Or, at least intrigued? Great. Here's our initial structure, accounting for every user input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// optional filename&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"l"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;snap_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// todo: handle Option types&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;list_devices&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;print_usage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid combination of arguments."&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By matching on &lt;code&gt;None&lt;/code&gt; in the &lt;code&gt;list_devices()&lt;/code&gt; and &lt;code&gt;print_usage()&lt;/code&gt; cases, we ensure that those arguments can only be used in isolation, otherwise the match will fail and we'll fall through to the "invalid combination" arm! Of course, in the &lt;code&gt;snap_image()&lt;/code&gt; case we're missing some &lt;code&gt;Option&lt;/code&gt;-handling, but let's assume that method is capable of accepting &lt;code&gt;None&lt;/code&gt; types and applying default values.&lt;/p&gt;

&lt;p&gt;What we're left with is an incredibly concise representation of the way the logic flows from inputs to actions/outcomes. Each arm summarizes a set of compatible inputs that lead to a specific behavior. We should avoid letting two arms lead to the same behavior, and rely on our type system/variants to abstract some of those details away.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding More Arms
&lt;/h3&gt;

&lt;p&gt;We may be missing a few corner cases, but accounting for them is simply a matter of adding a new pattern arm!&lt;/p&gt;

&lt;p&gt;One thing that immediately jumps out at me is that I've forgotten to handle extraneous arguments. If someone supplies two or more non-option arguments instead of one, I'd like to return an error. This can be accomplished by adding an additional match on the 2nd argument, and expecting it to be &lt;code&gt;None&lt;/code&gt; in all but the catch-all case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// optional filename&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="py"&gt;.free&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 2nd free arg should always be `None`&lt;/span&gt;
    &lt;span class="c1"&gt;// etc...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;snap_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// etc...&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid combination of arguments."&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another improvement I'd like to make is to parse and validate the "warmup" input, which must be cast to a float value. Once again, I can slot in a couple extra match arms, and I can even add an &lt;code&gt;if&lt;/code&gt; condition to the pattern match!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Transpose lets us match on the `Result` of the parse,&lt;/span&gt;
    &lt;span class="c1"&gt;// but preserve the `Option` type. `Ok(None)` is a valid input!&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="nf"&gt;.opt_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.transpose&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="c1"&gt;// etc...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Warmup must be between 0 and 10 seconds"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;snap_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to parse warmup!"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// etc...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;The joy of pattern matching makes it easy to get a little carried-away, so of course I'll want to be mindful of the complexity of this particular &lt;code&gt;match&lt;/code&gt;. Things like numerical validations can always be tucked-away into one of the arms. But for a simple program with only a few arguments, being able to express the full range of valid inputs up-front is extremely satisfying, and comes with compiler-enforced guarantees!&lt;/p&gt;

&lt;p&gt;And this isn't to say that the c-style &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; approach is inherently bad! I imagine that plenty of coders will see the above &lt;code&gt;match&lt;/code&gt; structure and start dry-heaving, and that's okay! Declarative programming takes some getting used to, and not everyone will prefer its aesthetics. But for those who do, hopefully this post has sparked some inspiration!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>patternmatching</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
