Rust Language Immersion Course


Last November someone I follow on Mastodon posted about taking a week-long Rust programming course that used the Crafting Interpreters book as the project. At the I wished I could join in the fun.

A couple weeks ago she posted that the course was being offered again, for the last time. After thinking about it for a couple days I decided to sign up. It’s a five day course, Monday-Friday, from 9:30 am - 5:30 pm. The bulk of the time is spent crafting a rusty interpreter. The day starts with an overview of the next challenge in the book, and some ideas on how to best use Rust to solve them. Periodically through the day, there are more “lectures”.

I have definitely been immersed. I’m typically up by 6 or 6:30 am, so by the time the course day ends at 5:30 pm, I’ve put in a good 10 hours of work. I end up spending a couple more hours in the evening. I haven’t done this much focused programming in a long time.

Yesterday I spent the day creating the Abstract Syntax Tree (AST) portion of the project. Then in the evening I re-did most of it. My programming roots are procedural. The first dozen or so years of my career were spent writing COBOL. I’ve worked in Forte TOOL (a defunct 4Gl), and a smattering of scripting languages.

Where I am lacking is knowing about the helpers and syntactic sugar Rust provides. Wrapping my head around traits, or enums has taken some effort. My brute-force struct-for-every-statement approach worked, but it had far too much boilerplate code. Forcing my self to make use of enums and iterators made the code far more compact, and ultimately better.

That I have been programming for more then 45 years and I am still learning new tricks, new languages, and new ways to do things, is amazing. I was a mediocre student all through school. If you had told me back then that I would delight in learning new things, even esoteric things that I won’t ever use, for the joy of it, I wouldn’t have believed you.

I have book from the 1990s called “Thinking Body, Dancing Mind” (I think that’s the title). Being able to immerse myself in a week-long course, writing a programming language interpreter, in Rust, for fun is most certainly a case of dancing mind.


49 Years and Still Operating ⧁


The Voyager spacecraft are incredible. Nearly 49 years after launch and they are still functioning and still transmitting data. Simply astonishing engineering.


Crusty Interpreter


Several years ago I discovered Writing an Interpreter in Go and the sequel Writing a Compiler in Go, by Thorsten Ball. I managed to get most of the way through the first book. Thorsten did a superb job of explaining the concepts behind tokens, lexers, and parsing.

Several weeks ago Lindsey Kuper (@lindsey@resurse.social) talked about a week long class in creating an interpreter in Rust. The road map for this is Crafting Interpreters by Robert Nystrom.

A week or so ago, when she reposted a message from the creator and teacher behind the “Crusty Interpreter” course, I really wanted to take it. I am fascinated by Rust as a programming language, and I’m equally fascinated by interpreters and compilers. After pondering it for a while I decided to sign up.

Crusty Interpreter is a week-long, immersion style course. The short description for the course:

Challenge yourself by implementing an interpreter for a programming language that you don’t know (Lox) in a programming language that you might not know (Rust) from a book that uses a programming language that a lot of people used to know (Java).

Sounds like a blast. I didn’t take the operating systems course in college, so this is a change to catch up.

I likely won’t blog much about it during the week, but I hope to recap it once the course is over. Lindsey wrote about her experience in the course.


Greatest Car Review Ever



§

For any prime number, p, where p is greater than or equal to 5, p²-1 is always divisible by 24.


The War in Iran ⧁


This is a sobering, but essential read. The spreading ripples from this colossal error in judgment, are going to last for years, and they are going to alter the lives of everyone on the planet.


§

All governments suffer a recurring problem: Power attracts pathalogical personalities. It is not that power corrupts but that it is magnetic to the corruptible.
~ Frank Herbert


Tolkien's Hymn to Humility and Mercy ⧁


Seventy years ago, in January or February 1956, J.R.R. Tolkien wrote a letter to Michael Straight, editor of New Republic, in which he mentioned a “ferocious” letter he had received some time before. His correspondent had argued that Frodo, the protagonist of The Lord of the Rings, should have been executed as a traitor, not praised. “The ‘salvation’ of the world and Frodo’s own ‘salvation’," Tolkien replied, “is achieved by his previous pity and forgiveness of injury.”


Rust Namespace Pattern


Rust doesn’t provide namespaces, nor does it have static classes. If you want to group related functions under a named “thing”, without any actual data, a unit struct is the idiomatic way to accomplish this. Essentially the struct is being used as a logical container or namespace for the functions. The pattern is also known as a Unit Struct pattern, or a Zero-Sized Type (ZST) facade.

Here’s what it looks like:

pub struct Foo;

impl Foo {
    pub fn bar() -> i32 { 43 }
    pub fn baz() -> &'static str { "hello" }
}

You could just have free functions in a module, but the unit struct pattern has some specific advantages:

  • Trait implementation: You can implement traits on a unit struct. This is the primary reason for using this pattern. For example, you could implement a Handler, Strategy, or Factory trait on Foo. Something you can’t do on loose functions.

  • It can be passed as a value: Foo is a concrete type, so you can pass it to functions that expect a generic T bounded by some trait. Or you can use it in a collection.

  • It signals intent: Grouping methods on a struct signals that they are conceptually related in a way that’s more explicit than just a module.

The zero-sized part matters. Because Foo holds no data, it costs nothing at runtime. std::mem::size_of::<Foo>() is 0. The compiler may eliminate any instance of it, so it’s a pure compile-time organizational tool with no overhead.

A real world example using the marker/strategy pattern

pub struct JsonSerializer;
pub struct XmlSerializer;

trait Serializer {
    fn serialize(data: &str) -> String;
}

impl Serializer for JsonSerializer {
    fn serialize(data: &str) -> String { format!("{{\"data\": \"{}\"}}", data) }
}

impl Serializer for XmlSerializer {
    fn serialize(data: &str) -> String { format!("<data>{}</data>", data) }
}

Now you can write generic code over T: Serializer and swap implementations at compile time with zero runtime costs.

To summarize: a unit struct, also called a ZST namespace/facade pattern, is used for trait implementation, type-level polymorphism, and logical grouping. All with zero runtime cost.


§

30 March 1973
Dear Mr. Nadeau:
As long as there is one upright man, as long as there is one compassionate woman, the contagion may spread and the scene is not desolate. Hope is the thing that is left to us, in a bad time. I shall get up on Sunday morning and wind the clock, as a contribution to order and steadfastness.
Sailors have an expression about the weather: they say, the weather is a greate bluffer. I guess the same is true of our human society-things can look dark, then a break shows in the clouds, and all is changed, sometimes rather suddenly. It is quite obvious that the human race has made a queer mess of life on this planet. But as a people we probably harbor seeds of goodness that have lain for a long time waiting to sprout when the conditions are right. Man’s curiosity, his relentlessness, his inventiveness, his ingenuity have led him into deep trouble. We can only hope that these same traits will enable him to claw his way out.
Hang on to your hat. Hang on to your hope. And wind the clock, for tomorrow is another day.
Sincerely,
E. B. White