Deadbeef? Just say no. Let's learn to build a small Rust app to find out what words can you spell with the letters A-F
Tags: rust, tutorial, commandline, friendlycode
Published: 2019-11-07 18:39:08

---
title: Deadbeef? Just say no. Let's learn to build a small Rust app to find out what words can you spell with the letters A-F
published: true
description: A Rust tutorial for creating a small command-line utility
tags: #rust #tutorial #commandline #friendlycode
---

I'm always really irritated when I see "babe" and "deadbeef" (the worst) in code examples that use hexadecimal numbers. It's actually not that funny.

You know you can do the dab in hex, right?

![Doing the Dab](https://thepracticaldev.s3.amazonaws.com/i/vl968jyo7yoo2jnz7gpm.jpg)

Photo: [User:Gokudabbing](https://commons.wikimedia.org/wiki/User:Gokudabbing) / [Wikipedia](https://en.wikipedia.org/wiki/Dab_(dance)#/media/File:Do_the_Dab.jpg)

What else is there? Here's a selection (the full list is at the bottom of the post).

**Nouns**

- ace (perhaps that should be an adjective, it's time for a 90s linguistic revival) 
- bead
- bee
- cab
- dad
- fad

**Adjectives**

- decaf
- deaf
- dead
- deed
- faded

**Verbs**

- add
- cede
- fade
- dab
- feed


## How do we find them?

Here's the full source code. If you're new to Rust, keep reading&mdashlI explain what's going on below.

```rust
use std::fs::File;
use std::io::{BufReader, BufRead};

fn main() -> std::io::Result<()> {
    let f = File::open("/usr/share/dict/words")?;
    let reader = BufReader::new(f);

    'lines: for line in reader.lines() {
        let word = line.unwrap();
        for byte in word.bytes() {
            match byte {
                b'A' | b'B' | b'C' | b'D' | b'E' | b'F' |
                b'a' | b'b' | b'c' | b'd' | b'e' | b'f' => continue,
                _ => continue 'lines,
            }
        }

        if word.len() > 2 {
            println!("{}", word);
        }
    };

    Ok(())
}

```

Let's break the example down. We start with imports.


```rust
use std::fs::File;
use std::io::{BufReader, BufRead};
```

We're bringing some of the standard library into local scope. That gives us the ability to open files. `File` can open them. A `BufReader` can read them efficiently. We also need to import `BufReader`'s "traits" along with it to avoid name clashes. Traits define methods, and we can sort of pick and mix the methods that we want for any given type.

> **Hint:** If you haven't encountered the term trait before, think of it as an Abstract Base Class or an interface. If you're new to programming and haven't heard of those two either, that's ok. It's not essential to know what a trait is for the rest of the post.   


```rust
fn main() -> std::io::Result<()> {
    // ...
    Ok(())
}
```

The `std::io::Result<()>` type is a bit crazy, sorry about that. It's an indicator that something might fail under the hood&mdash;like the hard drive might break or we could trigger a permissions error&mdash;and Rust should be prepared for that.

Adding `Ok(())` at the end is our `main()` function saying "everything went fine". We are returning `Ok` with a `()` type, which has the fun name "unit". It is a placeholder value.

```rust
let f = File::open("/usr/share/dict/words")?;
```

I run Ubuntu, which includes a list of valid-ish words as a plain text file. We open that up. If we do end up triggering a permissions error, or the file is deleted, the `?` at the end will propagate the error and immediately exit the `main()` function.

```rust
let reader = BufReader::new(f);
```

We create a `BufReader` that knows how to read from `File` objects efficiently. It's called `BufReader` because it has contains an internal in-memory buffer that can  and then ask it to inject anything it reads into the string we create next.


Now comes the best bits, or why I love Rust:

```rust
    'lines: for line in reader.lines() {
        let word = line.unwrap();
        for byte in word.bytes() {
            match byte {
                b'A' | b'B' | b'C' | b'D' | b'E' | b'F' |
                b'a' | b'b' | b'c' | b'd' | b'e' | b'f' => continue,
                _ => continue 'lines,
            }
        }
    
        if word.len() > 2 {
            println!("{}", word);
        }
    };
```

This chunk of code is a bit of a showoff. We define a named loop, `'lines` that can be used later to immediately abort from a word that contains a character outside of our limited alphabet. And then it uses the `match` syntax to elegantly match bytes that we care about. (The `b` prefix on all of those literals indicates to Rust that they should be treated as 8-bit integers, not as characters or strings - for the details check a Rust book) 

## Compiling the code

Let's see the result!

First let's build a new project:

```
$ cargo new hexwords
     Created binary (application) `hexwords` package
```

```
$ tree hexwords
hexwords
├── Cargo.toml
└── src
    └── main.rs
```

Now copy the source code into the `main.rs` file. Once that's done, we can run it.

```
$ cd hexwords

$ cargo run -q
Abe
Ada
BBB
Bede
Beebe
Dacca
Dada
Dec
Decca
Dee
Edda
Feb
abed
accede
acceded
ace
aced
add
added
baa
baaed
babe
bad
bade
bead
beaded
bed
bedded
bee
beef
beefed
cab
cabbed
cad
cede
ceded
dab
dabbed
dad
dead
deaf
deb
decade
decaf
deed
deeded
deface
defaced
ebb
ebbed
efface
effaced
facade
face
faced
fad
fade
faded
fed
fee
feed
```

Voilà!

Full source code here:

https://github.com/timClicks/hexwords