Say it ain't so - Exploring Rust Part II

August 4, 2024

The hardest thing in programming - picking a project

So for this project, I wanted to build a simple system with Windows APIs. So I spent 30 minutes perusing windows-rs and WinRT to see what kind of Windows features looked interesting to use. Some of the features that stood out to me included:

  • HID LampArray Device Lighting - I was considering making a lighting effect but since I don’t have any devices that support this protocol, this idea was tabled.
  • Windows Media Capture - I am interested in doing some video processing but I’ll likely save that for a later project since this will be a bit complicated as I might want to implement a DAG pipeline for processing the video in different ways.
  • Windows Media Face Analysis - I’ve used face/body tracking on a React Native project before so I considered remaking this in Rust, but asset/content generation would take time and was not something I was interested in doing.


Here’s a demo of win-say. Also, you may think I’m joking but this game actually exists, it’s called Emberward.

Enter win-say, a way for you to rest your vocal cords

In the end, Windows Media Speech Synthesis (a much cooler name than tts) won out and I decided to implement a bare bones version of the say command from MacOS. This was one of my favorite *nix commands when I first started programming.

# If you've never tried `say` before...
# Grab your MacBook, open Terminal, and type the following in, then hit enter:
say "Sitong sent me here"

# Actually, they might butcher my name so play around with the phonetics for me please.

If you’d like to see the code for win-say, check out the repository here.

Thoughts from this project

  • Error handling in Rust is a first class citizen. The language really forces you to consider error bubbling/propagation more than any language I’ve used before. This actually led to a lot of boilerplate code for me (match). Then boilerplate frustration actually led me to enable Github Copilot for some autocompletion snippets and forced me into a longer deep dive into Result<T, E> , unwrap, unwrap variants, and finally ? bubbling which finally let me avoid fighting the compiler as much.
  • Cargo features are a nice pattern to reduce unnecessary compilation of unneeded code. I can see why this would exist in the Rust world, where compilation times can get wild fast. However, as a Rust newbie, I definitely wasted a number of cycles wondering why my crate imports were causing compilation errors or wondering what was the name of the feature I needed to add to Cargo.toml. This still feels like a very manual process to me - if anyone has any tips here, please reach out to me.
  • I basically never think about multithreading due to working on the single threaded event loop Web. So it’s been great to reengage with threads and shared memory. There are a lot more patterns I’d like to see/implement here. This project was my first battle with Rust closures, variable lifetimes across threads, moving or borrowing, and it all has been enlightening in-between the periods of confusion/frustration.
  • Copilot is really great for snippets and saving me doc lookup time. For instance, I typed println!(”thread id and I was able to tab complete to println!("Thread id: {:?}", thread::current().id()); without needing to know the Rust standard library functions.
  • Working with Windows docs means 1000 tabs open - this I already knew though.

My other favorite *nix commands include: cowsay, lolcat, cmatrix, and sl

$ echo "this is a mood" | cowsay
 ________________
< this is a mood >
 ----------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||