New blogpost! (and some updates)
This commit is contained in:
parent
19aa58e531
commit
7ee9e8cb8e
37 changed files with 776 additions and 48 deletions
|
@ -39,7 +39,12 @@ background_color = "rosepine"
|
|||
logo_text = "CherryKitten"
|
||||
|
||||
copyright_html = """\
|
||||
<p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
"""
|
||||
|
||||
menu_items = [
|
||||
|
@ -53,3 +58,4 @@ menu_items = [
|
|||
]
|
||||
|
||||
page_titles = "combined"
|
||||
post_view_navigation_prompt = "More posts!"
|
||||
|
|
245
content/blog/rust_1_options_results.md
Normal file
245
content/blog/rust_1_options_results.md
Normal file
|
@ -0,0 +1,245 @@
|
|||
+++
|
||||
title = "Learning Rust Part 1: A kitten's guide to Options and Results"
|
||||
date = 2023-02-25
|
||||
[taxonomies]
|
||||
tags=["Rust", "programming", "code", "learning"]
|
||||
+++
|
||||
### To unwrap() or not to unwrap(), that is the question:
|
||||
|
||||
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)
|
||||
|
||||
There are some really pawesome guides out there, ["The Rust programming language"](https://doc.rust-lang.org/book/) is
|
||||
definitely a **must-read** in my opinion, and [Rustlings](https://github.com/rust-lang/rustlings) is nyamazing for
|
||||
anyone who likes to learn by actively working through interactive problems.
|
||||
|
||||
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 `Option` and `Result`. So this post will be about those, and hopefully will mark the start of a series on this blog.
|
||||
|
||||
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 "obvious". 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.
|
||||
|
||||
So, let's start!
|
||||
|
||||
<!-- more -->
|
||||
|
||||
Firstly, a very quick introduction. `Option` and `Result` are part of the Rust standard library. Quoting the official documentation
|
||||
is probably the easiest way to summarize their purpose:
|
||||
|
||||
> Type `Option` represents an optional value: every Option is either `Some` and contains a value, or `None`, and does not. `Option` types are very common in Rust code, as they have a number of uses:
|
||||
>
|
||||
> * Initial values
|
||||
> * Return values for functions that are not defined over their entire input range (partial functions)
|
||||
> * Return value for otherwise reporting simple errors, where `None` is returned on error
|
||||
> * Optional struct fields
|
||||
> * Struct fields that can be loaned or “taken”
|
||||
> * Optional function arguments
|
||||
> * Nullable pointers
|
||||
> * Swapping things out of difficult situations
|
||||
|
||||
and
|
||||
|
||||
> `Result<T, E>` is the type used for returning and propagating errors. It is an enum with the variants, `Ok(T)`, representing success and containing a value, and `Err(E)`, representing error and containing an error value.
|
||||
|
||||
At first, it seems so easy to just add a quick `.unwrap()` after every `Option` or `Result`, but this comes with
|
||||
the disadvantage of your code [panicking](https://doc.rust-lang.org/std/macro.panic.html) 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.
|
||||
|
||||
So, what can you do instead?
|
||||
|
||||
First of all, don't use `unwrap()` 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 `Ok` or `Some`.
|
||||
|
||||
In some cases, you actually want the program to panic. But even then, there is a slightly better way. You can use `expect("foo")`
|
||||
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 _expected_ to happen.
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = Some("Hello, World");
|
||||
// There is no actual "None" type/value in Rust,
|
||||
// this "None" is more specifically Option::None
|
||||
let y: Option<String> = None;
|
||||
|
||||
let z = x.unwrap(); // We explicitly set this to Some, so we can safely unwrap it
|
||||
let rip = y.expect("expected y to not be None");
|
||||
|
||||
println!("{}, {} will never print because it panics above", z, rip);
|
||||
}
|
||||
```
|
||||
|
||||
There are also the non-panicking siblings of `unwrap()`, like `unwrap_or()`, `unwrap_or_else()` and `unwrap_or_default()`.
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a: Option<String> = None;
|
||||
let b: Option<i32> = None;
|
||||
let c: Option<bool> = None;
|
||||
|
||||
// unwrap_or() lets you supply a specific value to use if the Option is None
|
||||
let x = a.unwrap_or("Hello there".to_owned());
|
||||
|
||||
// unwrap_or_default() uses the types default value if the Option is None
|
||||
let y = b.unwrap_or_default();
|
||||
|
||||
// unwrap_or_else() lets you specify a closure to run if the Option is None
|
||||
let z = c.unwrap_or_else(|| if 1 + 1 == 2 { true} else { false });
|
||||
|
||||
assert_eq!(x, "Hello there".to_owned());
|
||||
assert_eq!(y, 0);
|
||||
assert_eq!(z, true);
|
||||
}
|
||||
```
|
||||
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 `Result`s and `Option`s. The way it works is that, if you have a `None` or an `Error`,
|
||||
it passes up the handling of this one level higher, by returning out of the function early with a `None` or `Error` value itself.
|
||||
|
||||
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 `Result` or `Option`.
|
||||
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = 10;
|
||||
let z = 20;
|
||||
|
||||
match do_something(x, y, z) {
|
||||
Some(result) => println!("Happy noises, {}", result),
|
||||
None => println!("Sad noises"),
|
||||
}
|
||||
}
|
||||
|
||||
fn do_something(x: i32, y: i32, z: i32) -> Option<i32> {
|
||||
let first_result = do_something_more(x, y)?;
|
||||
let second_result = do_something_more(first_result, z)?;
|
||||
|
||||
Some(second_result)
|
||||
}
|
||||
|
||||
fn do_something_more(x: i32, y: i32) -> Option<i32> {
|
||||
Some(x + y)
|
||||
}
|
||||
```
|
||||
|
||||
The advantage of this is that you only have to handle your `None` case exactly once. You don't have to add pattern matching, or
|
||||
conditionals, or `unwrap()`s all over the place, just a cute little question mark that delegates the handling to some logic
|
||||
higher up.
|
||||
|
||||
_"But sammy!"_ you say, _"the compiler keeps shouting at me when I use the question mark on Options when my function
|
||||
returns Result \`<-.\_.->´"_
|
||||
|
||||
Don't worry my frien! Even this has been considered!
|
||||
|
||||
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 `Result` and `Option` are different types. Because of that, I thought I'd have to manually handle `None` cases in all
|
||||
of my `Result`-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 [Option::Ok_or()](https://doc.rust-lang.org/std/option/enum.Option.html#method.ok_or).
|
||||
|
||||
> Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
|
||||
|
||||
This was a life-changer to me, and it was just hiding right there in plain sight. Now I can easily turn a `None` where there shouldn't
|
||||
be a `None` into an `Error` to pass up with my pawesome question-mark operator!
|
||||
|
||||
```rust
|
||||
fn main() -> Result<(), String> {
|
||||
let x = function_that_returns_option().ok_or("error message".to_owned())?;
|
||||
// Instead of:
|
||||
// let x = function_that_returns_option().unwrap();
|
||||
// or any of the other ways to handle None
|
||||
|
||||
assert_eq!(x, ());
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
fn function_that_returns_option() -> Option<()> {
|
||||
return Some(());
|
||||
}
|
||||
```
|
||||
|
||||
The last thing I want to mention is both an example specific to `Option`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 collection of lints to catch common mistakes and improve your Rust code](https://doc.rust-lang.org/stable/clippy/).
|
||||
Clippy is automatically installed when you install Rust via `rustup`, and it runs a whole lot of checks against your code
|
||||
to tell you what you can improve.
|
||||
|
||||
In my case, I had the following piece of code:
|
||||
|
||||
```rust
|
||||
let insert = (
|
||||
tracks::title.eq(match tag.title() {
|
||||
Some(title) => Some(title.to_string()),
|
||||
None => None,
|
||||
}),
|
||||
tracks::track_number.eq(match tag.track() {
|
||||
Some(track) => Some(track as i32),
|
||||
None => None,
|
||||
}),
|
||||
tracks::disc_number.eq(match tag.disk() {
|
||||
Some(track) => Some(track as i32),
|
||||
None => None,
|
||||
}),
|
||||
tracks::path.eq(match path.to_str() {
|
||||
None => return Err(Error::msg("Could not get path")),
|
||||
Some(path) => path.to_string(),
|
||||
}),
|
||||
tracks::year.eq(match tag.year() {
|
||||
Some(year) => Some(year as i32),
|
||||
None => None,
|
||||
}),
|
||||
tracks::album_id.eq(match album {
|
||||
Some(album) => Some(album.id),
|
||||
None => None,
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
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 `Option`s, since the tags might be empty. The databse entries are also all `Option`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 `Some(value)` if there is a value, and `None`if there is none.
|
||||
|
||||
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:
|
||||
|
||||
> Manual implementation of `Option::map`
|
||||
|
||||
Huh, okay. Let's check the [documentation](https://doc.rust-lang.org/std/option/enum.Option.html#method.map)
|
||||
|
||||
> Maps an `Option<T>` to `Option<U>` by applying a function to a contained value.
|
||||
|
||||
So basically exactly what I did with those `match` statements!
|
||||
My IDE even had a button to just easily fix this automatically with one click:
|
||||
|
||||
```rust
|
||||
let insert = (
|
||||
tracks::title.eq(tag.title().map(|title| title.to_string())),
|
||||
tracks::track_number.eq(tag.track().map(|track| track as i32)),
|
||||
tracks::disc_number.eq(tag.disk().map(|track| track as i32)),
|
||||
tracks::path.eq(match path.to_str() {
|
||||
None => return Err(Error::msg("Could not get path")),
|
||||
Some(path) => path.to_string(),
|
||||
}),
|
||||
tracks::year.eq(tag.year().map(|year| year as i32)),
|
||||
tracks::album_id.eq(album.map(|album| album.id)),
|
||||
);
|
||||
```
|
||||
|
||||
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 `NOT NULL`, thus if the original `Option` is
|
||||
a `None` it means something went wrong, and we should abort this insert and return with an Error.
|
||||
|
||||
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.
|
||||
|
||||
Thank you so much for reading 💜
|
|
@ -1,5 +1,6 @@
|
|||
+++
|
||||
title = "Hello there!"
|
||||
template = "page.html"
|
||||
+++
|
||||
|
||||
{{ image(src="/profile.jpeg", alt="Profile picture",
|
|
@ -65,7 +65,12 @@
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -111,7 +111,12 @@ all the relevant projects in my <a href="https://codeberg.org/CherryKitten/freec
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -133,6 +133,22 @@ Welcome to the Fediverse 💜.</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 next">
|
||||
<a href="https://cherrykitten.dev/blog/rust-1-options-results/">
|
||||
<span class="button__text">Learning Rust Part 1: A kitten's guide to Options and Results</span>
|
||||
<span class="button__icon">→</span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -140,7 +156,12 @@ Welcome to the Fediverse 💜.</p>
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -57,6 +57,59 @@
|
|||
<div class="posts">
|
||||
<div class="post on-list">
|
||||
|
||||
<h1 class="post-title"><a href="https://cherrykitten.dev/blog/rust-1-options-results/">Learning Rust Part 1: A kitten'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:
|
||||
<a class="post-tag" href="https://cherrykitten.dev/tags/rust/">#Rust</a>
|
||||
<a class="post-tag" href="https://cherrykitten.dev/tags/programming/">#programming</a>
|
||||
<a class="post-tag" href="https://cherrykitten.dev/tags/code/">#code</a>
|
||||
<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/">"The Rust programming language"</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 "obvious". 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>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<!-- ︎ -- force text style - some devices render this as emoji -->
|
||||
<a class="read-more button" href="https://cherrykitten.dev/blog/rust-1-options-results/">
|
||||
<span class="button__text">Read more</span>
|
||||
<span class="button__icon">↩︎</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="post on-list">
|
||||
|
||||
<h1 class="post-title"><a href="https://cherrykitten.dev/blog/fediverse-isnt-just-mastodon/">The Fediverse is more than just Mastodon</a></h1>
|
||||
<div class="post-meta-inline">
|
||||
|
||||
|
@ -103,7 +156,12 @@ People have been recommending "Mastodon" as a Twitter alternative for
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
324
public/blog/rust-1-options-results/index.html
Normal file
324
public/blog/rust-1-options-results/index.html
Normal file
|
@ -0,0 +1,324 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Learning Rust Part 1: A kitten'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/rosepine.css">
|
||||
|
||||
<link rel="stylesheet" href="https://cherrykitten.dev/font-hack-subset.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'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:
|
||||
<a class="post-tag" href="https://cherrykitten.dev/tags/rust/">#Rust</a>
|
||||
<a class="post-tag" href="https://cherrykitten.dev/tags/programming/">#programming</a>
|
||||
<a class="post-tag" href="https://cherrykitten.dev/tags/code/">#code</a>
|
||||
<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/">"The Rust programming language"</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 "obvious". 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<T, E></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("foo")</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;">"Hello, World"</span><span>);
|
||||
</span><span> </span><span style="color:#403c58;">// There is no actual "None" type/value in Rust,
|
||||
</span><span> </span><span style="color:#403c58;">// this "None" 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><</span><span style="font-style:italic;color:#66d9ef;">String</span><span>> </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;">"expected y to not be None"</span><span>);
|
||||
</span><span>
|
||||
</span><span> println!(</span><span style="color:#f1ca93;">"</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"</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><</span><span style="font-style:italic;color:#66d9ef;">String</span><span>> </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><</span><span style="font-style:italic;color:#9bced7;">i32</span><span>> </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><</span><span style="font-style:italic;color:#9bced7;">bool</span><span>> </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;">"Hello there"</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;">"Hello there"</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;">=> </span><span>println!(</span><span style="color:#f1ca93;">"Happy noises, </span><span style="color:#c3a5e6;">{}</span><span style="color:#f1ca93;">"</span><span>, result),
|
||||
</span><span> </span><span style="font-style:italic;color:#66d9ef;">None </span><span style="color:#ea6f91;">=> </span><span>println!(</span><span style="color:#f1ca93;">"Sad noises"</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>) -> </span><span style="font-style:italic;color:#66d9ef;">Option</span><span><</span><span style="font-style:italic;color:#9bced7;">i32</span><span>> {
|
||||
</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>) -> </span><span style="font-style:italic;color:#66d9ef;">Option</span><span><</span><span style="font-style:italic;color:#9bced7;">i32</span><span>> {
|
||||
</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>"But sammy!"</em> you say, <em>"the compiler keeps shouting at me when I use the question mark on Options when my function
|
||||
returns Result `<-._.->´"</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<T></code> into a <code>Result<T, E></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>() -> </span><span style="font-style:italic;color:#66d9ef;">Result</span><span><(), </span><span style="font-style:italic;color:#66d9ef;">String</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:#66d9ef;">function_that_returns_option</span><span>().</span><span style="color:#66d9ef;">ok_or</span><span>(</span><span style="color:#f1ca93;">"error message"</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>() -> </span><span style="font-style:italic;color:#66d9ef;">Option</span><span><()> {
|
||||
</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;">=> </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;">=> </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;">=> </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;">=> </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;">=> </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;">=> </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;">=> return </span><span style="font-style:italic;color:#66d9ef;">Err</span><span>(Error::msg(</span><span style="color:#f1ca93;">"Could not get path"</span><span>)),
|
||||
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(path) </span><span style="color:#ea6f91;">=></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;">=> </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;">=> </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;">=> </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;">=> </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<T></code> to <code>Option<U></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;">=> return </span><span style="font-style:italic;color:#66d9ef;">Err</span><span>(Error::msg(</span><span style="color:#f1ca93;">"Could not get path"</span><span>)),
|
||||
</span><span> </span><span style="font-style:italic;color:#66d9ef;">Some</span><span>(path) </span><span style="color:#ea6f91;">=></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>
|
||||
<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"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1 +1 @@
|
|||
.button-container{display:table;margin-left:auto;margin-right:auto}button,.button,a.button{position:relative;display:flex;align-items:center;justify-content:center;padding:8px 18px;margin-bottom:5px;text-align:center;border-radius:8px;border:1px solid transparent;appearance:none;cursor:pointer;outline:none}button.outline,.button.outline,a.button.outline{background:transparent;box-shadow:none;padding:8px 18px}button.outline :hover,.button.outline :hover,a.button.outline :hover{transform:none;box-shadow:none}button.primary,.button.primary,a.button.primary{box-shadow:0 4px 6px rgba(50,50,93,0.11),0 1px 3px rgba(0,0,0,0.08)}button.primary:hover,.button.primary:hover,a.button.primary:hover{box-shadow:0 2px 6px rgba(50,50,93,0.21),0 1px 3px rgba(0,0,0,0.08)}button.link,.button.link,a.button.link{background:none;font-size:1rem}button.small,.button.small,a.button.small{font-size:.8rem}button.wide,.button.wide,a.button.wide{min-width:200px;padding:14px 24px}a.read-more,a.read-more:hover,a.read-more:active{display:inline-flex;background:none;box-shadow:none;padding:0;margin:20px 0;max-width:100%}.code-toolbar{margin-bottom:20px}.code-toolbar .toolbar-item a{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px 8px;margin-bottom:5px;text-align:center;font-size:13px;font-weight:500;border-radius:8px;border:1px solid transparent;appearance:none;cursor:pointer;outline:none}
|
||||
.button-container{display:table;margin-left:auto;margin-right:auto}button,.button,a.button{position:relative;display:flex;align-items:center;justify-content:center;padding:8px 18px;margin-bottom:5px;text-align:center;border-radius:8px;border:1px solid rgba(0,0,0,0);appearance:none;cursor:pointer;outline:none;}button.outline,.button.outline,a.button.outline{background:rgba(0,0,0,0);box-shadow:none;padding:8px 18px}button.outline :hover,.button.outline :hover,a.button.outline :hover{transform:none;box-shadow:none}button.primary,.button.primary,a.button.primary{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}button.primary:hover,.button.primary:hover,a.button.primary:hover{box-shadow:0 2px 6px rgba(50,50,93,.21),0 1px 3px rgba(0,0,0,.08)}button.link,.button.link,a.button.link{background:none;font-size:1rem}button.small,.button.small,a.button.small{font-size:.8rem}button.wide,.button.wide,a.button.wide{min-width:200px;padding:14px 24px}a.read-more,a.read-more:hover,a.read-more:active{display:inline-flex;background:none;box-shadow:none;padding:0;margin:20px 0;max-width:100%}.code-toolbar{margin-bottom:20px}.code-toolbar .toolbar-item a{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:3px 8px;margin-bottom:5px;text-align:center;font-size:13px;font-weight:500;border-radius:8px;border:1px solid rgba(0,0,0,0);appearance:none;cursor:pointer;outline:none}
|
|
@ -141,7 +141,12 @@
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*!
|
||||
* Hack typeface https://github.com/source-foundry/Hack
|
||||
* License: https://github.com/source-foundry/Hack/blob/master/LICENSE.md
|
||||
*/@font-face{font-family:'Hack';src:url("fonts/hack-regular.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-regular.woff?sha=3114f1256") format("woff");font-weight:400;font-style:normal}@font-face{font-family:'Hack';src:url("fonts/hack-bold-subset.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bold-subset.woff?sha=3114f1256") format("woff");font-weight:700;font-style:normal}@font-face{font-family:'Hack';src:url("fonts/hack-italic-subset.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-italic-webfont.woff?sha=3114f1256") format("woff");font-weight:400;font-style:italic}@font-face{font-family:'Hack';src:url("fonts/hack-bolditalic-subset.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bolditalic-subset.woff?sha=3114f1256") format("woff");font-weight:700;font-style:italic}
|
||||
*/@font-face{font-family:"Hack";src:url("fonts/hack-regular.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-regular.woff?sha=3114f1256") format("woff");font-weight:400;font-style:normal}@font-face{font-family:"Hack";src:url("fonts/hack-bold-subset.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bold-subset.woff?sha=3114f1256") format("woff");font-weight:700;font-style:normal}@font-face{font-family:"Hack";src:url("fonts/hack-italic-subset.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-italic-webfont.woff?sha=3114f1256") format("woff");font-weight:400;font-style:italic}@font-face{font-family:"Hack";src:url("fonts/hack-bolditalic-subset.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bolditalic-subset.woff?sha=3114f1256") format("woff");font-weight:700;font-style:italic}
|
|
@ -1,4 +1,4 @@
|
|||
/*!
|
||||
* Hack typeface https://github.com/source-foundry/Hack
|
||||
* License: https://github.com/source-foundry/Hack/blob/master/LICENSE.md
|
||||
*/@font-face{font-family:'Hack';src:url("fonts/hack-regular.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-regular.woff?sha=3114f1256") format("woff");font-weight:400;font-style:normal}@font-face{font-family:'Hack';src:url("fonts/hack-bold.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bold.woff?sha=3114f1256") format("woff");font-weight:700;font-style:normal}@font-face{font-family:'Hack';src:url("fonts/hack-italic.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-italic.woff?sha=3114f1256") format("woff");font-weight:400;font-style:italic}@font-face{font-family:'Hack';src:url("fonts/hack-bolditalic.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bolditalic.woff?sha=3114f1256") format("woff");font-weight:700;font-style:italic}
|
||||
*/@font-face{font-family:"Hack";src:url("fonts/hack-regular.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-regular.woff?sha=3114f1256") format("woff");font-weight:400;font-style:normal}@font-face{font-family:"Hack";src:url("fonts/hack-bold.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bold.woff?sha=3114f1256") format("woff");font-weight:700;font-style:normal}@font-face{font-family:"Hack";src:url("fonts/hack-italic.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-italic.woff?sha=3114f1256") format("woff");font-weight:400;font-style:italic}@font-face{font-family:"Hack";src:url("fonts/hack-bolditalic.woff2?sha=3114f1256") format("woff2"),url("fonts/hack-bolditalic.woff?sha=3114f1256") format("woff");font-weight:700;font-style:italic}
|
|
@ -1 +1 @@
|
|||
.footer{padding:40px 0;flex-grow:0;opacity:.5}.footer__inner{display:flex;align-items:center;justify-content:space-between;margin:0;width:760px;max-width:100%}@media (max-width: 899px){.footer__inner{flex-direction:column}}.footer a{color:inherit}.footer .copyright{display:flex;flex-direction:row;align-items:center;font-size:1rem;color:var(--light-color-secondary)}.footer .copyright--user{margin:auto;text-align:center}.footer .copyright>*:first-child:not(:only-child){margin-right:10px}@media (max-width: 899px){.footer .copyright>*:first-child:not(:only-child){border:none;padding:0;margin:0}}@media (max-width: 899px){.footer .copyright{flex-direction:column;margin-top:10px}}@media (max-width: 899px){.footer .copyright-theme-sep{display:none}}@media (max-width: 899px){.footer .copyright-theme{font-size:0.75rem}}
|
||||
.footer{padding:40px 0;flex-grow:0;opacity:.5}.footer__inner{display:flex;align-items:center;justify-content:space-between;margin:0;width:760px;max-width:100%}@media (max-width: 899px){.footer__inner{flex-direction:column}}.footer a{color:inherit}.footer .copyright{display:flex;flex-direction:row;align-items:center;font-size:1rem;color:var(--light-color-secondary)}.footer .copyright--user{margin:auto;text-align:center}.footer .copyright>*:first-child:not(:only-child){margin-right:10px}@media (max-width: 899px){.footer .copyright>*:first-child:not(:only-child){border:none;padding:0;margin:0}}@media (max-width: 899px){.footer .copyright{flex-direction:column;margin-top:10px}}@media (max-width: 899px){.footer .copyright-theme-sep{display:none}}@media (max-width: 899px){.footer .copyright-theme{font-size:.75rem}}
|
|
@ -1 +1 @@
|
|||
.header{display:flex;flex-direction:column;position:relative}.header__inner{display:flex;align-items:center;justify-content:space-between}.header__logo{display:flex;flex:1}.header__logo:after{content:'';background:repeating-linear-gradient(90deg, var(--accent), var(--accent) 2px, transparent 0, transparent 16px, var(--accent2), var(--accent2) 17px, transparent 0, transparent 32px);display:block;width:100%;right:10px}.header__logo a{flex:0 0 auto;max-width:100%}.header .menu{margin:20px 0}.header .menu__inner{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.header .menu__inner li{color:var(--accent3)}.header .menu__inner li.active{color:var(--accent-alpha-70)}.header .menu__inner li:not(:last-of-type){margin-right:20px;margin-bottom:10px;flex:0 0 auto}.header .menu__sub-inner{position:relative;list-style:none;padding:0;margin:0}.header .menu__sub-inner:not(:only-child){margin-left:20px}.header .menu__sub-inner-more{position:absolute;background:var(--background);box-shadow:var(--shadow);color:white;border:2px solid;margin:0;padding:10px;list-style:none;z-index:99;top:35px;left:0}.header .menu__sub-inner-more-trigger{color:var(--accent);user-select:none;cursor:pointer}.header .menu__sub-inner-more li{margin:0;padding:5px;white-space:nowrap}
|
||||
.header{display:flex;flex-direction:column;position:relative}.header__inner{display:flex;align-items:center;justify-content:space-between}.header__logo{display:flex;flex:1}.header__logo:after{content:"";background:repeating-linear-gradient(90deg, var(--accent), var(--accent) 2px, rgba(0,0,0,0) 0, rgba(0,0,0,0) 16px, var(--accent2), var(--accent2) 17px, rgba(0,0,0,0) 0, rgba(0,0,0,0) 32px);display:block;width:100%;right:10px}.header__logo a{flex:0 0 auto;max-width:100%}.header .menu{margin:20px 0}.header .menu__inner{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.header .menu__inner li{color:var(--accent3)}.header .menu__inner li.active{color:var(--accent-alpha-70)}.header .menu__inner li:not(:last-of-type){margin-right:20px;margin-bottom:10px;flex:0 0 auto}.header .menu__sub-inner{position:relative;list-style:none;padding:0;margin:0}.header .menu__sub-inner:not(:only-child){margin-left:20px}.header .menu__sub-inner-more{position:absolute;background:var(--background);box-shadow:var(--shadow);color:#fff;border:2px solid;margin:0;padding:10px;list-style:none;z-index:99;top:35px;left:0}.header .menu__sub-inner-more-trigger{color:var(--accent);user-select:none;cursor:pointer}.header .menu__sub-inner-more li{margin:0;padding:5px;white-space:nowrap}
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
<div class="post">
|
||||
|
||||
<h1 class="post-title"><a href="https://cherrykitten.dev/content/">Hello there!</a></h1>
|
||||
<h1 class="post-title"><a href="https://cherrykitten.dev/home/">Hello there!</a></h1>
|
||||
<div class="post-meta-inline">
|
||||
|
||||
<span class="post-date"></span>
|
||||
|
@ -96,7 +96,12 @@
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
|
@ -89,7 +89,12 @@
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
<div class="posts">
|
||||
<div class="post on-list">
|
||||
|
||||
<h1 class="post-title"><a href="https://cherrykitten.dev/content/">Hello there!</a></h1>
|
||||
<h1 class="post-title"><a href="https://cherrykitten.dev/home/">Hello there!</a></h1>
|
||||
<div class="post-meta-inline">
|
||||
|
||||
<span class="post-date"></span>
|
||||
|
@ -100,7 +100,12 @@
|
|||
|
||||
<footer class="footer">
|
||||
<div class="footer__inner">
|
||||
<div class="copyright copyright--user"><p>© {currentYear} - CherryKitten</p><a href="/impressum">Impressum</a>
|
||||
<div class="copyright copyright--user"><p>© 2023 - CherryKitten</p><br>
|
||||
<p><a href="/impressum">Impressum</a></p>
|
||||
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
|
||||
🐱
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -1 +1 @@
|
|||
.logo{display:flex;align-items:center;text-decoration:none;background:var(--accent);color:black;padding:5px 10px}
|
||||
.logo{display:flex;align-items:center;text-decoration:none;background:var(--accent);color:#000;padding:5px 10px}
|
|
@ -1 +1 @@
|
|||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}body{margin:0;padding:0;font-family:Hack, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;font-size:1rem;line-height:1.54;background-color:var(--background);color:var(--color);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%}@media (max-width: 683px){body{font-size:1rem}}h1,h2,h3,h4,h5,h6{display:flex;align-items:center;font-weight:bold;line-height:1.3;color:var(--accent2)}h1{font-size:1.4rem}h2{font-size:1.3rem}h3{font-size:1.2rem}h4,h5,h6{font-size:1.15rem}a{color:inherit}img{display:block;max-width:100%}img.left{margin-right:auto}img.center{margin-left:auto;margin-right:auto}img.right{margin-left:auto}p{margin-bottom:20px}figure{display:table;max-width:100%;margin:25px 0}figure.left img{margin-right:auto}figure.center img{margin-left:auto;margin-right:auto}figure.right img{margin-left:auto}figure figcaption{font-size:14px;padding:5px 10px;margin-top:5px;background:var(--accent);color:var(--background)}figure figcaption.left{text-align:left}figure figcaption.center{text-align:center}figure figcaption.right{text-align:right}code{font-family:Hack, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;font-feature-settings:normal;background:var(--accent-alpha-20);padding:1px 6px;margin:0 2px;font-size:.95rem}pre{font-family:Hack, DejaVu Sans Mono, Monaco, Consolas, Ubuntu Mono, monospace;padding:20px;font-size:.95rem;overflow:auto;border-top:1px solid rgba(255,255,255,0.1);border-bottom:1px solid rgba(255,255,255,0.1)}@media (max-width: 683px){pre{white-space:pre-wrap;word-wrap:break-word}}pre code{padding:0;margin:0;background:none}blockquote{border-top:1px solid var(--accent);border-bottom:1px solid var(--accent);margin:40px 0;padding:25px}@media (max-width: 683px){blockquote{padding-right:0}}blockquote:before{content:'”';font-family:Georgia, serif;font-size:3.875rem;position:absolute;left:-40px;top:-20px}blockquote p:first-of-type{margin-top:0}blockquote p:last-of-type{margin-bottom:0}blockquote p{position:relative}blockquote p:before{content:'>';display:block;position:absolute;left:-25px;color:var(--accent)}table{table-layout:fixed;border-collapse:collapse;width:100%;margin:40px 0}table,th,td{border:1px dashed var(--accent);padding:10px}th{color:var(--accent)}ul,ol{margin-left:30px;padding:0}ul li,ol li{position:relative}@media (max-width: 683px){ul,ol{margin-left:20px}}ol ol{list-style-type:lower-alpha}.container{display:flex;flex-direction:column;padding:40px;max-width:864px;min-height:100vh;margin:0 auto}@media (max-width: 683px){.container{padding:20px}}.content{display:flex}hr{width:100%;border:none;background:var(--border-color);height:1px}.hidden{display:none}
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}body{margin:0;padding:0;font-family:Hack,DejaVu Sans Mono,Monaco,Consolas,Ubuntu Mono,monospace;font-size:1rem;line-height:1.54;background-color:var(--background);color:var(--color);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%}@media (max-width: 683px){body{font-size:1rem}}h1,h2,h3,h4,h5,h6{display:flex;align-items:center;font-weight:bold;line-height:1.3;color:var(--accent2)}h1{font-size:1.4rem}h2{font-size:1.3rem}h3{font-size:1.2rem}h4,h5,h6{font-size:1.15rem}a{color:inherit}img{display:block;max-width:100%}img.left{margin-right:auto}img.center{margin-left:auto;margin-right:auto}img.right{margin-left:auto}p{margin-bottom:20px}figure{display:table;max-width:100%;margin:25px 0}figure.left img{margin-right:auto}figure.center img{margin-left:auto;margin-right:auto}figure.right img{margin-left:auto}figure figcaption{font-size:14px;padding:5px 10px;margin-top:5px;background:var(--accent);color:var(--background)}figure figcaption.left{text-align:left}figure figcaption.center{text-align:center}figure figcaption.right{text-align:right}code{font-family:Hack,DejaVu Sans Mono,Monaco,Consolas,Ubuntu Mono,monospace;font-feature-settings:normal;background:var(--accent-alpha-20);padding:1px 6px;margin:0 2px;font-size:.95rem}pre{font-family:Hack,DejaVu Sans Mono,Monaco,Consolas,Ubuntu Mono,monospace;padding:20px;font-size:.95rem;overflow:auto;border-top:1px solid rgba(255,255,255,.1);border-bottom:1px solid rgba(255,255,255,.1)}@media (max-width: 683px){pre{white-space:pre-wrap;word-wrap:break-word}}pre code{padding:0;margin:0;background:none}blockquote{border-top:1px solid var(--accent);border-bottom:1px solid var(--accent);margin:40px 0;padding:25px}@media (max-width: 683px){blockquote{padding-right:0}}blockquote:before{content:"”";font-family:Georgia,serif;font-size:3.875rem;position:absolute;left:-40px;top:-20px}blockquote p:first-of-type{margin-top:0}blockquote p:last-of-type{margin-bottom:0}blockquote p{position:relative}blockquote p:before{content:">";display:block;position:absolute;left:-25px;color:var(--accent)}table{table-layout:fixed;border-collapse:collapse;width:100%;margin:40px 0}table,th,td{border:1px dashed var(--accent);padding:10px}th{color:var(--accent)}ul,ol{margin-left:30px;padding:0}ul li,ol li{position:relative}@media (max-width: 683px){ul,ol{margin-left:20px}}ol ol{list-style-type:lower-alpha}.container{display:flex;flex-direction:column;padding:40px;max-width:864px;min-height:100vh;margin:0 auto}@media (max-width: 683px){.container{padding:20px}}.content{display:flex}hr{width:100%;border:none;background:var(--border-color);height:1px}.hidden{display:none}
|
|
@ -1 +1 @@
|
|||
.posts{width:100%;margin:0 auto}.post{width:100%;text-align:left;margin:20px auto;padding:20px 0}@media (max-width: 899px){.post{max-width:660px}}.post:not(:last-of-type){border-bottom:1px solid var(--border-color)}.post .post-meta-inline,.post .post-meta{font-size:1rem;margin-bottom:10px;color:var(--accent-alpha-70)}.post-meta-inline{display:inline}.post-title{--border: 2px dashed var(--accent);position:relative;color:var(--accent2);margin:0 0 15px;padding-bottom:15px;border-bottom:var(--border);font-weight:normal}.post-title a{text-decoration:none}.post .post-tags-inline,.post .post-tags{margin-bottom:20px;font-size:1rem;opacity:.5}.post-tags{display:block}.post-tags-inline{display:inline}@media (max-width: 683px){.post-tags-inline{display:block}}.post-content{margin-top:30px}.post-cover{border:20px solid var(--accent);background:transparent;margin:40px 0;padding:20px}@media (max-width: 683px){.post-cover{padding:10px;border-width:10px}}.post ul{list-style:none}.post ul li:before{content:'►';position:absolute;left:-20px;color:var(--accent)}.post--regulation h1{justify-content:center}.post--regulation h2{justify-content:center;margin-bottom:10px}.post--regulation h2+h2{margin-top:-10px;margin-bottom:20px}.post-list .post-date{color:var(--accent3);text-decoration:none}.post-list a{text-decoration:none}.post-list .post-list-title{text-decoration:underline}.post-list .post-tag{text-decoration:underline}
|
||||
.posts{width:100%;margin:0 auto}.post{width:100%;text-align:left;margin:20px auto;padding:20px 0}@media (max-width: 899px){.post{max-width:660px}}.post:not(:last-of-type){border-bottom:1px solid var(--border-color)}.post .post-meta-inline,.post .post-meta{font-size:1rem;margin-bottom:10px;color:var(--accent-alpha-70)}.post-meta-inline{display:inline}.post-title{--border: 2px dashed var(--accent);position:relative;color:var(--accent2);margin:0 0 15px;padding-bottom:15px;border-bottom:var(--border);font-weight:normal}.post-title a{text-decoration:none}.post .post-tags-inline,.post .post-tags{margin-bottom:20px;font-size:1rem;opacity:.5}.post-tags{display:block}.post-tags-inline{display:inline}@media (max-width: 683px){.post-tags-inline{display:block}}.post-content{margin-top:30px}.post-cover{border:20px solid var(--accent);background:rgba(0,0,0,0);margin:40px 0;padding:20px}@media (max-width: 683px){.post-cover{padding:10px;border-width:10px}}.post ul{list-style:none}.post ul li:before{content:"►";position:absolute;left:-20px;color:var(--accent)}.post--regulation h1{justify-content:center}.post--regulation h2{justify-content:center;margin-bottom:10px}.post--regulation h2+h2{margin-top:-10px;margin-bottom:20px}.post-list .post-date{color:var(--accent3);text-decoration:none}.post-list a{text-decoration:none}.post-list .post-list-title{text-decoration:underline}.post-list .post-tag{text-decoration:underline}
|
|
@ -7,10 +7,37 @@
|
|||
<generator>Zola</generator>
|
||||
<language>en</language>
|
||||
<atom:link href="https://cherrykitten.dev/rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<lastBuildDate>Sat, 26 Nov 2022 00:00:00 +0000</lastBuildDate>
|
||||
<lastBuildDate>Sat, 25 Feb 2023 00:00:00 +0000</lastBuildDate>
|
||||
<item>
|
||||
<title>Learning Rust Part 1: A kitten's guide to Options and Results</title>
|
||||
<pubDate>Sat, 25 Feb 2023 00:00:00 +0000</pubDate>
|
||||
<author>Unknown</author>
|
||||
<link>https://cherrykitten.dev/blog/rust-1-options-results/</link>
|
||||
<guid>https://cherrykitten.dev/blog/rust-1-options-results/</guid>
|
||||
<description><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>
|
||||
</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>The Fediverse is more than just Mastodon</title>
|
||||
<pubDate>Sat, 26 Nov 2022 00:00:00 +0000</pubDate>
|
||||
<author>Unknown</author>
|
||||
<link>https://cherrykitten.dev/blog/fediverse-isnt-just-mastodon/</link>
|
||||
<guid>https://cherrykitten.dev/blog/fediverse-isnt-just-mastodon/</guid>
|
||||
<description><p>With Twitter in a downwards spiral thanks to some rich guy whose name escapes me breaking everything, the Fediverse has been getting the biggest influx of new users in a very long time.
|
||||
|
|
|
@ -16,11 +16,15 @@
|
|||
<url>
|
||||
<loc>https://cherrykitten.dev/blog/page/1/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/blog/rust-1-options-results/</loc>
|
||||
<lastmod>2023-02-25</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/contact/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/content/</loc>
|
||||
<loc>https://cherrykitten.dev/home/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/impressum/</loc>
|
||||
|
@ -34,13 +38,25 @@
|
|||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/calckey/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/code/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/fediverse/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/learning/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/mastodon/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/misskey/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/programming/</loc>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://cherrykitten.dev/tags/rust/</loc>
|
||||
</url>
|
||||
</urlset>
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue