Writing Rust … by Hand. No LLMs.
I recently had a bit of a self-identity crisis; of course, it was tied to spending way too much time on social media. I do try to avoid the worst of it, got rid of Twitter (X) this year, and mostly stick to Substack, LinkedIn, YouTube, occasional Reddit, and Hacker News. Probably still too much.
I’ve always seen it as a form of self-preservation, probably due to growing up the youngest of five in the middle of the prairie next to the river, in the only county without a working stoplight.
I try to be pragmatic and never dogmatic, except when someone says “Production” and “Notebook” in the same sentence.
Oh, back to my identity crisis. I probably just need to turn off social media, but I’m constantly torn about my future as a data and code person. In my heart of hearts, I believe things will be fine for those who care for their craft, as applied to the business of software, but that could just be wishful thinking.
Again, I digress; what I’m trying to say is that I miss writing code. Myself. Without a machine looking over my shoulder. Rust, I miss that nasty little borrow checker and compiler. I miss struggling.
If life writing code for two decades has taught me anything, it’s that struggle and exercising one’s mind are critical to success, personal and professional, and I’ve felt it sorta creeping up on me, slowly and sneakily.
But I can only accomplish those tasks well insofar as my current mind is continually exercised and acquainted with the intricacies of software used to solve data problems and provide value. Building systems that run well, are cost-effective, and are generally a pleasure to work with.
I, for one, find it surprising how quickly those finer details seem to fog over in the Cold Claude War we find ourselves in. Sure, it’s hard to erase 20+ years of code and experience, but it can get a little opaque.
So today, I’m here to do a simple thing with you. Bust out the terminal, vim, Rust, cargo … and solve a problem I deal with daily and find annoying. The most important part is that my own two fingers, with a few Google searches, are going to do this work.
WARNING! No LLMs were used in generating the following code.
The problem.
The problem is simple: I need the transcripts from my video and podcast recordings. For all sorts of reasons. I need the transcript to summarize and find …
- What I/we talked about, aka topics covered
- figure out a good title and description
- etc
There are a lot of AI tools I can pay for to do this for me, but I want my own, My Precious. So, we are going to whip out Rust and bang our heads against a wall until we figure this out.
cargo init transcription-rs
cargo add unbundle
I’m thinking we just want to accomplish a few simple things …
- command-line CLI to run this tool
- reduce dependencies
- rip out audio media from a video file
- … try to transcribe the audio file
I’m excited to write some Rust code without any LLMs, to think through the problems and struggle on my own, like the good old days.
Re-learning Rust without LLMs.
Let’s start our journey by using this crate to do something simple, knowing it probably won’t be our final solution. But can we open an MP4 video file, a clip from one of my podcasts, and have an audio file ripped from it?
use unbundle::{self, MediaFile, AudioFormat};
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let input_file = &args[1];
println!("Starting to work on {input_file}");
let video_data = unbundle::MediaFile::open(&input_file).expect("Failed to open video file");
process_video(video_data);
}
fn process_video(mut video_data: MediaFile) {
video_data.audio().save("output.wav", AudioFormat::Wav).unwrap();
}
We will probably need to use clap for proper CLI features at some point, but raw arg input parsing will work for now. So far, it’s pretty straightforward, at least in proving out our idea.
When it comes to mutable and immutable, we can think of our data and variables in such terms. This is far from the land of Python, where everything can be anything and do whatever. Also, another Rust beauty is borrowing: can we borrow a copy of a pointer to some data or thing?
If you work with data, you should train yourself to think of data points like Rust does: Is this piece of data changeable? Should it ever change? Does it become something else if I change or consume it? Where is the data used and how is it used?
It forces a deeper understanding of the data problem space you’re working on.
Well, our hacky little Rust script works; here is our output.wav audio file ripped from a video MP4.
Next, transcription: hackey is fine, but we need to pull text from the audio file.
Transcription with whisper-rs.
One thing I enjoy about hand-rolling code is not handing off the thinking part to our benevolent master Cladius Maximus. Working on scratching out a version of what I want requires me to do my own research into what others have found useful for solving problems.
The problem with The Vibes is that we don’t question the LLM’s choices enough. We tab and move on. What causes the invisible gremlins, the weights trained in some dark evil frontier lab, to pick one tool over another for solving a problem?
- Those reasons won’t be the same for a human.
OpenAI has a model, Whisper, that is a general-purpose speech recognition. Look, maybe we will turn back to the models, but not for our code.
Looks like we have some pretty small Whisper models available, which is great; we can probably use anything from tiny to base.
Of course, we could find the Rust wrapper for this tool, basically the Rust bindings to whisper.cpp
Ok, so let’s see if we can add some more hacky Rust code to get whisper-rs to extract a transcript from our audio file. Don’t worry, we will revisit the code at some point to do some refactoring, but I’m mostly just thinking about getting the end-to-end working at the moment.
- Doing things “right” can come later.
You can see that not much has changed, just the addition of whisper. When we process_video we return the raw audio, then we chunk that raw audio data out into a Vector, this allows us to pull_transcript with whisper.
This is hilarious.
I live in the capital of Twerba and Trutar. [growling] I am a woman. I am... my wife.
[sigh] [growling] [growling] [growling]
So, apparently, we have something running end-to-end, although I can tell you this is not how the conversation went. Maybe with the exception of “my wife. [sigh].”
Clearly, we have a frigging in the rigging somewhere, but I see this as a positive: the fact we have ~50 lines of Rust code and can take a video file and get something that is supposed to be a transcript out … well, that’s fun!
I’m no audio or video expert, but one can imagine this has something to do with the audio or video format, probably the audio being pulled from the video. From what I can tell, the Whisper model requires “16 kHz sample rate, 16-bit samples for audio.”
- Great, now I, who know nothing, need to figure out how to resample audio data.
Of course, a quick Google search shows rubato, a small Rust package we can use to resample.
Can you imagine some Rust wizard sitting in a dark basement somewhere who thought it was fun to write a Rust crate for resampling audio? Good Lord.
Anyways, with these two update/new methods, let’s re-run our script and see if we can get a real transcript back, and not “I live in the capital of Twerba and Trutar. [growling].”
Well, look at that! We got our own decent video-to-text transcription tool! Of course, we could have used a slower, more accurate Whisper model, but this looks good enough for what I need! All I need to do is maybe add another command-line argument to write the transcript to a text file.
Thoughts and more.
Well, that was fun, wasn’t it? A little wandering down memory lane to the good old days of fighting the borrow checker and compiler. Trust me, there was plenty of that going on that I didn’t show you.
To be honest, it was good to have those old feelings of not being able to figure something out right away. It took me two days, off and on, during my free time to hack this code together. Remember to exercise my brain about data, mutable or not, borrow it or not, data types … you know … just programming stuff.
Sure, I bet Claude could write this Rust code way better than I, much prettier, catch all the rough edges, who knows what path it would have chosen. That’s ok. I enjoyed the process of researching and thinking myself. Not worried about anyone else but me.
It was a nice reminder of minimalist code: not everything needs a ton of boilerplate, “to be done right,” or anything fancy. Seems like Rust is like riding a bike; it comes back awful quick. Maybe I will make it a habit.

