Category: Rust

I reread Robin Sloan’s post about a “home-cooked” app, and it continues to resonate; there’s something special about writing software for very small audiences. In that vein, here’s something that my partner and I have been using since last year:

We needed a solution for shared notes that works well across Android, iOS, and the (desktop) web… so I made one. We use it a lot, mostly for things like our grocery list and cooking notes. It’s simple, fast, and it works for us.

Technology-wise, it’s just a website that works well on desktop and mobile. The back-end is written in Rust, it uses HTMX for some dynamic updates, and the note contents get saved to a SQLite database (backed up to S3 with Litestream). The whole thing compiles down to a single-file executable that gets run on a cheap VPS.

Many corners were cut during the development of this project! Proper collaborative text editing is hard, and I took a quick-and-dirty approach. Eventually I’ll get it working better with Automerge or whatever, but for now I have something good enough for a user base of 2.

And now it’s really easy to do stuff with our shared notes:

That’s a cheap 7" e-ink display with a Raspberry Pi on the back, showing a customized view of our grocery list plus enough information to answer the question “do I need an umbrella today?”. It’s like a little household dashboard that we can take a quick look at as we head out the door. Eventually I need to make a frame for it and mount it on the wall, but it’s pretty handy as-is.

Why do all this?

Shared notes are a solved problem, I’m sure I could have found an existing service for this. But:

  • I like having a project to tinker on for someone I care for, in the same way that I like cooking for them
  • This is permanent in a way that cloud services are not. It will never change unless we want it to, and it will will work for decades without much effort. We might be using this in a retirement home one day
  • It’s trivial to tweak and extend software I wrote myself. I didn’t need to figure out an API to get the the e-ink display, I just built a new HTML view on top of existing data
  • Software designed for an audience of 2 is able to be much simpler than software that anticipates the needs of a large+diverse user base

Bonus: Burninator.exe

Now that I’ve shown you something useful, here’s some much sillier home-cooked software:

A friend asked:

What is a program that I can run in the background to raise the temperature of a laptop? If my laptop gets too cold the screen starts to flicker…

I wasn’t aware of any, so I wrote one that queries WMI for the current CPU temperature and then does busy work until the temperature is high enough. Is it dumb? Yes. Does it solve this one person’s very specific problem? Also yes!

It’s been a good summer so far. Some things I’ve been up to:

systemctl-tui

I got annoyed that I couldn’t find a decent GUI for systemd services, so I built one:

It was partially an excuse to experiment with ratatui for terminal UIs in Rust, and I think I like it quite a bit. ratatui gives you tools you need to do immediate-mode rendering efficiently, but the broad strokes of your application’s architecture are up to you.

Gardening + Patio

I spent more time than usual on our little outdoor space. Lots of flowers, a new planter, pressure washing, etc.

I’ve organized a fair number of get-togethers/dinners on the patio, it’s been really nice meeting new people and catching up with people who I haven’t seen since before the pandemic.

New ‘Puter

I finally took the plunge and bought a Framework Laptop:

Intel’s multicore performance has gotten a lot better in recent generations, so I figured I could replace my desktop for Rust work. So far, so good!

I like the hardware a lot. There were some teething pains getting Linux working properly, but that kinda comes with the territory.

I recently participated in the Handmade Network Visibility Jam and built a tool that I’d been wanting for my own use:

In a nutshell, Escape Artist shows ANSI escape sequences that are normally invisible. It’s kinda like View Source for the terminal; my hope is that it will be useful for anyone working on shells and terminal applications.

Tech Stack

Escape Artist is primarily written in Rust, using Axum to serve up a web UI using Tailwind+Preact. Events get streamed to the front-end over a websocket. I took great pains to keep the front-end simple; nearly all business logic lives in the Rust back-end, there is no front-end build step, and all dependencies are bundled with the application.

Escape Artist compiles down to a single-file 1.8MB executable, and nearly half of that is Tailwind and an embedded font.

Lessons Learned

Parsing escape sequences out of a stream of bytes is remarkably tricky. Thankfully the vte crate does most of the heavy lifting for us.

This minimal approach to web UI was generally pretty nice! I occasionally ran into a bit of trouble wrangling JS libraries that assume everyone is using NPM, React, and a bundler like webpack, but overall it wasn’t too bad.

I ended up needing to roll my own tooltip solution using Floating UI, and… wow. Their introductory tutorial is one of the best I’ve ever seen for a library in any language.

Recent Nushell/Rust Work

SQLite, file watcher, windows-rs

I’ve joined the Nushell core team. This doesn’t really change what I’m doing day-to-day, but it makes my work on Nu feel a little more official 🙂.

SQLite Support

This is the biggest feature I’ve implemented so far:

I’m pretty proud of how this turned out; it’s very convenient to be able to browse SQLite databases in your shell and interact with them the same way you would any other data source. Nu is often-but-not-always smart enough to avoid unnecessary work when loading things from the database; there’s still some work to do here and it will probably involve rearchitecting how Nushell queries data.

File watcher

I also implemented a watch command that runs arbitrary Nu code in response to file changes. Nothing groundbreaking, but I find myself needing this kind of low-key automation all the time: run tests when code changes, restart a development server, log changes in a directory, etc. I think the ability to respond to file changes should be a more widely available primitive, and now it is.

Rust for Windows

Against all odds, I somehow got sucked back into Windows development. I spent a solid week helping one of Nushell’s dependencies do a big upgrade of their Windows functionality. This required a deep dive into the current state of calling Windows APIs from Rust, and… it’s a mixed bag.

I used the windows crate which is maintained by Microsoft. It’s an automatically generated set of Rust bindings for Windows APIs, which is both good (very comprehensive, always kept up to date) and bad (some rough edges that might be solved in a handmade solution like winapi). The crate is actively being worked on and it frequently has breaking changes; this means documentation is a little scarce and often out of date. Overall I was impressed and I think the crate has a bright future . But until it settles down a bit, expect some growing pains.

I’ve been using Rust full time for the last month and a bit while contributing to Nushell (more on that later). A lot has changed since I first tried Rust in 2019 and this is my first time working on a big Rust project. Here are some thoughts on the language while they’re still fresh in my head.

Compile times and feedback loops

Rust’s compile times are notoriously slow. Rust development was slow enough on my laptop that I finally gave up on mobile computing and bought a desktop with a top-of-the line CPU (12900K). Along the way I switched from Windows to Linux (more on that later) and started using the mold linker, and now… things are OK!

I’m able to do incremental builds of Nushell (a huge project) in a second or 2, and a full debug build takes 25s. For smaller projects, incremental builds are nearly instant. There’s certainly room to improve here, and the development experience is not great on average hardware, but… this works for me.

Another thing to consider is that the typical Rust feedback loop is tighter than you might expect from the slow compile times. The Rust compiler catches a lot of bugs before a full build needs to happen, and that reduces the need to do a full build and try things out.

Complexity + monotony

Rust is not a simple language. In total I’ve spent nearly 3 months working mostly in Rust, and the language still has a lot of corners that I don’t have a solid grasp on. To improve on this I’m going to need to branch out from Nushell and write a lot of little tools for myself.

Despite the complexity, I’ve found that writing Rust is sometimes a bit… braindead? The type system is very expressive and the compiler catches a ton of errors, so I spend 25% of my time thinking real hard and 75% painting by numbers to make the compiler happy. I can’t quite decide how I feel about this style of development, it can be a little tedious but it also makes for a better end product.

I (sometimes) want a higher-level Rust

Rust has a lot of great things going for it; the tooling, community, package ecosystem, compiler, and syntax are all fantastic. But the focus on systems programming does mean that Rust isn’t quite as ergonomic as it could be for many use cases.

Sometimes I just want a garbage collector! Sometimes I’d be perfectly happy for Rust to implicitly allocate memory if it makes my code work (for example: converting from a &str to a String)! I don’t know if that will ever be possible in standard Rust, but… maybe there’s room for a Rust variant intended for higher-level use cases.

On the other hand, the ability to go as low as you want is great. It’s nice to work in a language with a very “high ceiling”; no matter where your Rust project goes, you won’t have to switch to C or C++.

headshot

Cities & Code

Top Categories

View all categories