cherrykitten.dev/public/blog/rust-1-options-results/index.html
2023-04-10 00:13:12 +02:00

330 lines
31 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<title>Learning Rust Part 1: A kitten&#x27;s guide to Options and Results | CherryKitten</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
<meta name="robots" content="noodp"/>
<link rel="stylesheet" href="https://cherrykitten.dev/style.css">
<link rel="stylesheet" href="https://cherrykitten.dev/color.css">
<link rel="alternate" type="application/rss+xml" title="RSS" href="https://cherrykitten.dev/ rss.xml
">
</head>
<body class="">
<div class="container">
<header class="header">
<div class="header__inner">
<div class="header__logo">
<a href="https://cherrykitten.dev" style="text-decoration: none;">
<div class="logo">
CherryKitten
</div>
</a>
</div>
</div>
<nav class="menu">
<ul class="menu__inner">
<li><a href="https://cherrykitten.dev">Home</a></li>
<li><a href="https://cherrykitten.dev/about">About me</a></li>
<li class="active"><a href="https://cherrykitten.dev/blog">Blog</a></li>
<li><a href="https://cherrykitten.dev/contact">Contact</a></li>
</ul>
</nav>
</header>
<div class="content">
<div class="post">
<h1 class="post-title"><a href="https://cherrykitten.dev/blog/rust-1-options-results/">Learning Rust Part 1: A kitten&#x27;s guide to Options and Results</a></h1>
<div class="post-meta-inline">
<span class="post-date">
2023-02-25
</span>
</div>
<span class="post-tags-inline">
:: tags:&nbsp;
<a class="post-tag" href="https://cherrykitten.dev/tags/rust/">#Rust</a>&nbsp;
<a class="post-tag" href="https://cherrykitten.dev/tags/programming/">#programming</a>&nbsp;
<a class="post-tag" href="https://cherrykitten.dev/tags/code/">#code</a>&nbsp;
<a class="post-tag" href="https://cherrykitten.dev/tags/learning/">#learning</a></span>
<div class="post-content">
<h3 id="to-unwrap-or-not-to-unwrap-that-is-the-question">To unwrap() or not to unwrap(), that is the question:</h3>
<p>So I've finally given in and started to learn Rust last month. It's a really cool programming language,
with some interesting differences to what I've used before. (JavaScript and Python, mostly)</p>
<p>There are some really pawesome guides out there, <a href="https://doc.rust-lang.org/book/">&quot;The Rust programming language&quot;</a> is
definitely a <strong>must-read</strong> in my opinion, and <a href="https://github.com/rust-lang/rustlings">Rustlings</a> is nyamazing for
anyone who likes to learn by actively working through interactive problems.</p>
<p>After reading through a lot of those big thorough guides by experienced Rust developers, I've started working on
my first actual Project. I approached the development of this project by just trying to get small parts of it
working in any way I can manage, and then build upon this. In that process, I learned a lot of small subtilties that
guides like the ones named above just can't really cover. This post is for sharing those things, those cool little
tips to make your first Rust project just a little cleaner and more Rust-y. Originally I wanted to make this about a lot
of different topics, but then I've realized that my notes already contain so many things about just one part of Rust:
The Enums <code>Option</code> and <code>Result</code>. So this post will be about those, and hopefully will mark the start of a series on this blog.</p>
<p>While reading through this, you might think that the things I'm mentioning are obvious. That's okay, and that's the point.
Nothing is ever completely obvious to everyone, and this is for those like me, who often don't immediately recognize
the &quot;obvious&quot;. And, to be honest, I am writing this just as much for myself, writing all of that stuff down to aid me in my
own ongoing learning process.</p>
<p>So, let's start!</p>
<span id="continue-reading"></span>
<p>Firstly, a very quick introduction. <code>Option</code> and <code>Result</code> are part of the Rust standard library. Quoting the official documentation
is probably the easiest way to summarize their purpose:</p>
<blockquote>
<p>Type <code>Option</code> represents an optional value: every Option is either <code>Some</code> and contains a value, or <code>None</code>, and does not. <code>Option</code> types are very common in Rust code, as they have a number of uses:</p>
<ul>
<li>Initial values</li>
<li>Return values for functions that are not defined over their entire input range (partial functions)</li>
<li>Return value for otherwise reporting simple errors, where <code>None</code> is returned on error</li>
<li>Optional struct fields</li>
<li>Struct fields that can be loaned or “taken”</li>
<li>Optional function arguments</li>
<li>Nullable pointers</li>
<li>Swapping things out of difficult situations</li>
</ul>
</blockquote>
<p>and</p>
<blockquote>
<p><code>Result&lt;T, E&gt;</code> is the type used for returning and propagating errors. It is an enum with the variants, <code>Ok(T)</code>, representing success and containing a value, and <code>Err(E)</code>, representing error and containing an error value.</p>
</blockquote>
<p>At first, it seems so easy to just add a quick <code>.unwrap()</code> after every <code>Option</code> or <code>Result</code>, but this comes with
the disadvantage of your code <a href="https://doc.rust-lang.org/std/macro.panic.html">panicking</a> if it fails to unwrap.
Sometimes, this can be useful during development, to discover potential error cases you might not have thought about, but
is usually not what you want to happen.</p>
<p>So, what can you do instead?</p>
<p>First of all, don't use <code>unwrap()</code> unless you are completely sure that the value will never panic. Sometimes that is the
case, because an earlier part of your code already made sure that it is <code>Ok</code> or <code>Some</code>.</p>
<p>In some cases, you actually want the program to panic. But even then, there is a slightly better way. You can use <code>expect(&quot;foo&quot;)</code>
to add a message to the panic, so the user actually knows what went wrong. That message should be worded in a specific way,
basically telling the user what you <em>expected</em> to happen.</p>
<pre data-lang="rust" style="background-color:#1f1d29;color:#ffffff;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#9bced7;">fn </span><span style="color:#34738e;">main</span><span>() {
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> x </span><span style="color:#ea6f91;">= </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(</span><span style="color:#f1ca93;">&quot;Hello, World&quot;</span><span>);
</span><span> </span><span style="color:#403c58;">// There is no actual &quot;None&quot; type/value in Rust,
</span><span> </span><span style="color:#403c58;">// this &quot;None&quot; is more specifically Option::None
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> y: </span><span style="font-style:italic;color:#66d9ef;">Option</span><span>&lt;</span><span style="font-style:italic;color:#66d9ef;">String</span><span>&gt; </span><span style="color:#ea6f91;">= </span><span style="font-style:italic;color:#66d9ef;">None</span><span>;
</span><span>
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> z </span><span style="color:#ea6f91;">=</span><span> x.</span><span style="color:#66d9ef;">unwrap</span><span>(); </span><span style="color:#403c58;">// We explicitly set this to Some, so we can safely unwrap it
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> rip </span><span style="color:#ea6f91;">=</span><span> y.</span><span style="color:#66d9ef;">expect</span><span>(</span><span style="color:#f1ca93;">&quot;expected y to not be None&quot;</span><span>);
</span><span>
</span><span> println!(</span><span style="color:#f1ca93;">&quot;</span><span style="color:#c3a5e6;">{}</span><span style="color:#f1ca93;">, </span><span style="color:#c3a5e6;">{}</span><span style="color:#f1ca93;"> will never print because it panics above&quot;</span><span>, z, rip);
</span><span>}
</span></code></pre>
<p>There are also the non-panicking siblings of <code>unwrap()</code>, like <code>unwrap_or()</code>, <code>unwrap_or_else()</code> and <code>unwrap_or_default()</code>.</p>
<pre data-lang="rust" style="background-color:#1f1d29;color:#ffffff;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#9bced7;">fn </span><span style="color:#34738e;">main</span><span>() {
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> a: </span><span style="font-style:italic;color:#66d9ef;">Option</span><span>&lt;</span><span style="font-style:italic;color:#66d9ef;">String</span><span>&gt; </span><span style="color:#ea6f91;">= </span><span style="font-style:italic;color:#66d9ef;">None</span><span>;
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> b: </span><span style="font-style:italic;color:#66d9ef;">Option</span><span>&lt;</span><span style="font-style:italic;color:#9bced7;">i32</span><span>&gt; </span><span style="color:#ea6f91;">= </span><span style="font-style:italic;color:#66d9ef;">None</span><span>;
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> c: </span><span style="font-style:italic;color:#66d9ef;">Option</span><span>&lt;</span><span style="font-style:italic;color:#9bced7;">bool</span><span>&gt; </span><span style="color:#ea6f91;">= </span><span style="font-style:italic;color:#66d9ef;">None</span><span>;
</span><span>
</span><span> </span><span style="color:#403c58;">// unwrap_or() lets you supply a specific value to use if the Option is None
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> x </span><span style="color:#ea6f91;">=</span><span> a.</span><span style="color:#66d9ef;">unwrap_or</span><span>(</span><span style="color:#f1ca93;">&quot;Hello there&quot;</span><span>.</span><span style="color:#66d9ef;">to_owned</span><span>());
</span><span>
</span><span> </span><span style="color:#403c58;">// unwrap_or_default() uses the types default value if the Option is None
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> y </span><span style="color:#ea6f91;">=</span><span> b.</span><span style="color:#66d9ef;">unwrap_or_default</span><span>();
</span><span>
</span><span> </span><span style="color:#403c58;">// unwrap_or_else() lets you specify a closure to run if the Option is None
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> z </span><span style="color:#ea6f91;">=</span><span> c.</span><span style="color:#66d9ef;">unwrap_or_else</span><span>(|| </span><span style="color:#ea6f91;">if </span><span style="color:#c3a5e6;">1 </span><span style="color:#ea6f91;">+ </span><span style="color:#c3a5e6;">1 </span><span style="color:#ea6f91;">== </span><span style="color:#c3a5e6;">2 </span><span>{ </span><span style="color:#c3a5e6;">true</span><span>} </span><span style="color:#ea6f91;">else </span><span>{ </span><span style="color:#c3a5e6;">false </span><span>});
</span><span>
</span><span> assert_eq!(x, </span><span style="color:#f1ca93;">&quot;Hello there&quot;</span><span>.</span><span style="color:#66d9ef;">to_owned</span><span>());
</span><span> assert_eq!(y, </span><span style="color:#c3a5e6;">0</span><span>);
</span><span> assert_eq!(z, </span><span style="color:#c3a5e6;">true</span><span>);
</span><span>}
</span></code></pre>
<p>And then there is this really cool question-mark operator, which comes in very handy once you go multiple functions deep
and keep having to work with more and more <code>Result</code>s and <code>Option</code>s. The way it works is that, if you have a <code>None</code> or an <code>Error</code>,
it passes up the handling of this one level higher, by returning out of the function early with a <code>None</code> or <code>Error</code> value itself.</p>
<p>Of course, since return types of functions have to be known at compile time, the question-mark operator only works inside
functions that already return <code>Result</code> or <code>Option</code>.</p>
<pre data-lang="rust" style="background-color:#1f1d29;color:#ffffff;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#9bced7;">fn </span><span style="color:#34738e;">main</span><span>() {
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> x </span><span style="color:#ea6f91;">= </span><span style="color:#c3a5e6;">5</span><span>;
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> y </span><span style="color:#ea6f91;">= </span><span style="color:#c3a5e6;">10</span><span>;
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> z </span><span style="color:#ea6f91;">= </span><span style="color:#c3a5e6;">20</span><span>;
</span><span>
</span><span> </span><span style="color:#ea6f91;">match </span><span style="color:#66d9ef;">do_something</span><span>(x, y, z) {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(result) </span><span style="color:#ea6f91;">=&gt; </span><span>println!(</span><span style="color:#f1ca93;">&quot;Happy noises, </span><span style="color:#c3a5e6;">{}</span><span style="color:#f1ca93;">&quot;</span><span>, result),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; </span><span>println!(</span><span style="color:#f1ca93;">&quot;Sad noises&quot;</span><span>),
</span><span> }
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#9bced7;">fn </span><span style="color:#34738e;">do_something</span><span>(</span><span style="font-style:italic;color:#f1ca93;">x</span><span>: </span><span style="font-style:italic;color:#9bced7;">i32</span><span>, </span><span style="font-style:italic;color:#f1ca93;">y</span><span>: </span><span style="font-style:italic;color:#9bced7;">i32</span><span>, </span><span style="font-style:italic;color:#f1ca93;">z</span><span>: </span><span style="font-style:italic;color:#9bced7;">i32</span><span>) -&gt; </span><span style="font-style:italic;color:#66d9ef;">Option</span><span>&lt;</span><span style="font-style:italic;color:#9bced7;">i32</span><span>&gt; {
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> first_result </span><span style="color:#ea6f91;">= </span><span style="color:#66d9ef;">do_something_more</span><span>(x, y)</span><span style="color:#ea6f91;">?</span><span>;
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> second_result </span><span style="color:#ea6f91;">= </span><span style="color:#66d9ef;">do_something_more</span><span>(first_result, z)</span><span style="color:#ea6f91;">?</span><span>;
</span><span>
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(second_result)
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#9bced7;">fn </span><span style="color:#34738e;">do_something_more</span><span>(</span><span style="font-style:italic;color:#f1ca93;">x</span><span>: </span><span style="font-style:italic;color:#9bced7;">i32</span><span>, </span><span style="font-style:italic;color:#f1ca93;">y</span><span>: </span><span style="font-style:italic;color:#9bced7;">i32</span><span>) -&gt; </span><span style="font-style:italic;color:#66d9ef;">Option</span><span>&lt;</span><span style="font-style:italic;color:#9bced7;">i32</span><span>&gt; {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(x </span><span style="color:#ea6f91;">+</span><span> y)
</span><span>}
</span></code></pre>
<p>The advantage of this is that you only have to handle your <code>None</code> case exactly once. You don't have to add pattern matching, or
conditionals, or <code>unwrap()</code>s all over the place, just a cute little question mark that delegates the handling to some logic
higher up.</p>
<p><em>&quot;But sammy!&quot;</em> you say, <em>&quot;the compiler keeps shouting at me when I use the question mark on Options when my function
returns Result `&lt;-._.-&gt;´&quot;</em></p>
<p>Don't worry my frien! Even this has been considered!</p>
<p>First of all, why does the compiler get upset? It's because the question-mark operator returns the same type that it's used on,
and <code>Result</code> and <code>Option</code> are different types. Because of that, I thought I'd have to manually handle <code>None</code> cases in all
of my <code>Result</code>-returning functions. Until one day, I was reading through some documentation (I know, I know, I'm a nerd who reads
through documentation for fun and not just to find specific things) and discovered <a href="https://doc.rust-lang.org/std/option/enum.Option.html#method.ok_or">Option::Ok_or()</a>.</p>
<blockquote>
<p>Transforms the <code>Option&lt;T&gt;</code> into a <code>Result&lt;T, E&gt;</code>, mapping <code>Some(v)</code> to <code>Ok(v)</code> and <code>None</code> to <code>Err(err)</code>.</p>
</blockquote>
<p>This was a life-changer to me, and it was just hiding right there in plain sight. Now I can easily turn a <code>None</code> where there shouldn't
be a <code>None</code> into an <code>Error</code> to pass up with my pawesome question-mark operator!</p>
<pre data-lang="rust" style="background-color:#1f1d29;color:#ffffff;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#9bced7;">fn </span><span style="color:#34738e;">main</span><span>() -&gt; </span><span style="font-style:italic;color:#66d9ef;">Result</span><span>&lt;(), </span><span style="font-style:italic;color:#66d9ef;">String</span><span>&gt; {
</span><span> </span><span style="font-style:italic;color:#9bced7;">let</span><span> x </span><span style="color:#ea6f91;">= </span><span style="color:#66d9ef;">function_that_returns_option</span><span>().</span><span style="color:#66d9ef;">ok_or</span><span>(</span><span style="color:#f1ca93;">&quot;error message&quot;</span><span>.</span><span style="color:#66d9ef;">to_owned</span><span>())</span><span style="color:#ea6f91;">?</span><span>;
</span><span> </span><span style="color:#403c58;">// Instead of:
</span><span> </span><span style="color:#403c58;">// let x = function_that_returns_option().unwrap();
</span><span> </span><span style="color:#403c58;">// or any of the other ways to handle None
</span><span>
</span><span> assert_eq!(x, ());
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Ok</span><span>(x)
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#9bced7;">fn </span><span style="color:#34738e;">function_that_returns_option</span><span>() -&gt; </span><span style="font-style:italic;color:#66d9ef;">Option</span><span>&lt;()&gt; {
</span><span> </span><span style="color:#ea6f91;">return </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(());
</span><span>}
</span></code></pre>
<p>The last thing I want to mention is both an example specific to <code>Option</code>s, and a more general tip about how I discovered this one.
There is this wonderful friend for all Rust developers, called Clippy. No, not the Paperclip from Microsoft Word, but
<a href="https://doc.rust-lang.org/stable/clippy/">A collection of lints to catch common mistakes and improve your Rust code</a>.
Clippy is automatically installed when you install Rust via <code>rustup</code>, and it runs a whole lot of checks against your code
to tell you what you can improve.</p>
<p>In my case, I had the following piece of code:</p>
<pre data-lang="rust" style="background-color:#1f1d29;color:#ffffff;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#9bced7;">let</span><span> insert </span><span style="color:#ea6f91;">= </span><span>(
</span><span> tracks::title.</span><span style="color:#66d9ef;">eq</span><span>(</span><span style="color:#ea6f91;">match</span><span> tag.</span><span style="color:#66d9ef;">title</span><span>() {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(title) </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(title.</span><span style="color:#66d9ef;">to_string</span><span>()),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">None</span><span>,
</span><span> }),
</span><span> tracks::track_number.</span><span style="color:#66d9ef;">eq</span><span>(</span><span style="color:#ea6f91;">match</span><span> tag.</span><span style="color:#66d9ef;">track</span><span>() {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(track) </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(track </span><span style="color:#ea6f91;">as </span><span style="font-style:italic;color:#9bced7;">i32</span><span>),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">None</span><span>,
</span><span> }),
</span><span> tracks::disc_number.</span><span style="color:#66d9ef;">eq</span><span>(</span><span style="color:#ea6f91;">match</span><span> tag.</span><span style="color:#66d9ef;">disk</span><span>() {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(track) </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(track </span><span style="color:#ea6f91;">as </span><span style="font-style:italic;color:#9bced7;">i32</span><span>),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">None</span><span>,
</span><span> }),
</span><span> tracks::path.</span><span style="color:#66d9ef;">eq</span><span>(</span><span style="color:#ea6f91;">match</span><span> path.</span><span style="color:#66d9ef;">to_str</span><span>() {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; return </span><span style="font-style:italic;color:#66d9ef;">Err</span><span>(Error::msg(</span><span style="color:#f1ca93;">&quot;Could not get path&quot;</span><span>)),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(path) </span><span style="color:#ea6f91;">=&gt;</span><span> path.</span><span style="color:#66d9ef;">to_string</span><span>(),
</span><span> }),
</span><span> tracks::year.</span><span style="color:#66d9ef;">eq</span><span>(</span><span style="color:#ea6f91;">match</span><span> tag.</span><span style="color:#66d9ef;">year</span><span>() {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(year) </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(year </span><span style="color:#ea6f91;">as </span><span style="font-style:italic;color:#9bced7;">i32</span><span>),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">None</span><span>,
</span><span> }),
</span><span> tracks::album_id.</span><span style="color:#66d9ef;">eq</span><span>(</span><span style="color:#ea6f91;">match</span><span> album {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(album) </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(album.id),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; </span><span style="font-style:italic;color:#66d9ef;">None</span><span>,
</span><span> }),
</span><span> );
</span></code></pre>
<p>This code builds an insert statement for the database holding my music metadata, getting the values from the tags of a file.
The tag fields are all <code>Option</code>s, since the tags might be empty. The databse entries are also all <code>Option</code>s, (at least on the Rust side,
on the database they are just values marked as possibly being Null). So my intuitive idea to build this was to just go through all the entries,
match the tag, put in <code>Some(value)</code> if there is a value, and <code>None</code>if there is none.</p>
<p>It works, it's not wrong, but there is a cleaner and more readable way to do this. And clippy told me right away, I ran it
from my IDE, and it told me:</p>
<blockquote>
<p>Manual implementation of <code>Option::map</code></p>
</blockquote>
<p>Huh, okay. Let's check the <a href="https://doc.rust-lang.org/std/option/enum.Option.html#method.map">documentation</a> </p>
<blockquote>
<p>Maps an <code>Option&lt;T&gt;</code> to <code>Option&lt;U&gt;</code> by applying a function to a contained value.</p>
</blockquote>
<p>So basically exactly what I did with those <code>match</code> statements!
My IDE even had a button to just easily fix this automatically with one click:</p>
<pre data-lang="rust" style="background-color:#1f1d29;color:#ffffff;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#9bced7;">let</span><span> insert </span><span style="color:#ea6f91;">= </span><span>(
</span><span> tracks::title.</span><span style="color:#66d9ef;">eq</span><span>(tag.</span><span style="color:#66d9ef;">title</span><span>().</span><span style="color:#66d9ef;">map</span><span>(|</span><span style="font-style:italic;color:#f1ca93;">title</span><span>| title.</span><span style="color:#66d9ef;">to_string</span><span>())),
</span><span> tracks::track_number.</span><span style="color:#66d9ef;">eq</span><span>(tag.</span><span style="color:#66d9ef;">track</span><span>().</span><span style="color:#66d9ef;">map</span><span>(|</span><span style="font-style:italic;color:#f1ca93;">track</span><span>| track </span><span style="color:#ea6f91;">as </span><span style="font-style:italic;color:#9bced7;">i32</span><span>)),
</span><span> tracks::disc_number.</span><span style="color:#66d9ef;">eq</span><span>(tag.</span><span style="color:#66d9ef;">disk</span><span>().</span><span style="color:#66d9ef;">map</span><span>(|</span><span style="font-style:italic;color:#f1ca93;">track</span><span>| track </span><span style="color:#ea6f91;">as </span><span style="font-style:italic;color:#9bced7;">i32</span><span>)),
</span><span> tracks::path.</span><span style="color:#66d9ef;">eq</span><span>(</span><span style="color:#ea6f91;">match</span><span> path.</span><span style="color:#66d9ef;">to_str</span><span>() {
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=&gt; return </span><span style="font-style:italic;color:#66d9ef;">Err</span><span>(Error::msg(</span><span style="color:#f1ca93;">&quot;Could not get path&quot;</span><span>)),
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(path) </span><span style="color:#ea6f91;">=&gt;</span><span> path.</span><span style="color:#66d9ef;">to_string</span><span>(),
</span><span> }),
</span><span> tracks::year.</span><span style="color:#66d9ef;">eq</span><span>(tag.</span><span style="color:#66d9ef;">year</span><span>().</span><span style="color:#66d9ef;">map</span><span>(|</span><span style="font-style:italic;color:#f1ca93;">year</span><span>| year </span><span style="color:#ea6f91;">as </span><span style="font-style:italic;color:#9bced7;">i32</span><span>)),
</span><span> tracks::album_id.</span><span style="color:#66d9ef;">eq</span><span>(album.</span><span style="color:#66d9ef;">map</span><span>(|</span><span style="font-style:italic;color:#f1ca93;">album</span><span>| album.id)),
</span><span> );
</span></code></pre>
<p>Great, that looks a lot cleaner immediately!
Note how one of the lines was not changed, that's because that one sets a DB value which is <code>NOT NULL</code>, thus if the original <code>Option</code> is
a <code>None</code> it means something went wrong, and we should abort this insert and return with an Error.</p>
<p>And with that, we're done with my first blogpost about Rust, with hopefully many more to come!
As I said, I am still learning, and writing this is part of my learning process. That being said, if you find this interesting,
learned something from it, etc., feel free to leave me some feedback! I'd love to hear what you think!
And if I made mistakes, please also tell me. I'm always happy to learn more and to fix those mistakes so others can learn from them too.</p>
<p>Thank you so much for reading 💜</p>
</div>
<div class="pagination">
<div class="pagination__title">
<span class="pagination__title-h">More posts!</span>
<hr />
</div>
<div class="pagination__buttons">
<span class="button previous">
<a href="https://cherrykitten.dev/blog/fediverse-isnt-just-mastodon/">
<span class="button__icon"></span>&nbsp;
<span class="button__text">The Fediverse is more than just Mastodon</span>
</a>
</span>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="footer__inner">
<div class="copyright copyright--user">
<a href="https://liberapay.com/CherryKitten/donate"><img
alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
 
<span>&copy;
2023
- CherryKitten</span><br>
 
<span><a href="/impressum">Impressum</a></span>
 
<span><a href="/rss.xml">RSS</a></span>
 
<span onclick="alert('Nya!')">🐱</span></div>
</div>
</footer>
</div>
</body>
</html>