(Previously on An Algorithmic Lucidity.)
The other weekend, excited to learn more and connect with people about what's going on at the forefront of expressive, performant, data-race-free computing—and eager for a healthy diversion from the last two months of agonizing delirium induced by the world-shattering insight about how everything I've cared about for the past fourteen years turns out to be related in unexpected and terrifying ways that I can't talk about for reasons that I also can't talk about—I took Friday off from my dayjob and caught a Thursday night flight out of SFO to exotic Portland (... I, um, don't travel much) for RustConf!
The conference itself was on Saturday, but Friday featured special training sessions run by members of the Rust core team! I was registered for Niko Matsakis's afternoon session on lifetimes, but I arrived at the venue (the Luxury Collection Nines Hotel) early to get registered (I had never seen socks as conference swag before!) and hang out with folks and get a little bit of coding done: my coolest Rust project so far is a chess engine that I wrote this time last year (feel free to go ahead and give it a Star!) which I wanted the option to show off (Option<ShowOff>
) to other conference attendees, but the pretty web application frontend had broken due to a recent bug and my JavaScript build pipeline having rotted. I fixed it just in time for the lifetimes training session to start.
Every reference (I kind of want to say ampersand) in Rust code has an associated lifetime, the region of the program that that reference is valid for. Lifetime annotations (appearing in angle brackets like generics and starting with an apostrophe; by convention, usually named consecutively from the start of the lowercase Latin alphabet: 'a
, 'b
, &c.) in function signatures are used to distinguish between the lifetimes of different reference arguments, but the compiler has lifetime ellision rules that cover the 90% use-cases, so you can actually write pretty substantial Rust programs without actually understanding the theory, which is both practically useful and eternally shameful (for a programmer who is satisfied with not understanding something is not long for this world). Hence the training. (Exercises from the training sessions are available online.)
The point of lifetime analysis is to ensure that all references point somewhere valid; you can't (can't, the compiler won't let you) have a reference to a thing that outlives the thing itself. When you return a reference from a function, you can't be referencing something created by that function, because any such thing would die at the end of the function as it goes out of scope: a reference in the return type has to be a reference to something owned by the caller that was passed as an argument, but if there was more than one reference argument, it's ambiguous which of the reference arguments has to be outlived by the returned reference, which is why you sometimes need explicit lifetime annotations ...
Um, it's complicated. (Maybe this is just one of those things no one knows how to teach, and you just have to pick it up by osmosis or spend a week auditing the relevant part of the compiler source??)
Lifetimes are currently bound to lexical scopes, which are sometimes much bigger than we actually want, bigger than we could get away with if the compiler was smarter, so sometimes the borrow checker will reject code that a human can see is actually safe. Borrowing is like a compile-time readers-writer lock; you can have many readers or at most one writer at the same time. Consequently, when running into a spurious ("spurious") borrow checker error, Matsakis recommends separating your code into distinct query and act operations. The result of the query must not be a reference into the thing you're operating on (that would be holding the reader lock!) but it can be a value, or an index into the thing that you use as a kind of pseudo-reference. (I was reminded of how I was looking up how to implement graphs back in March because I wanted to implement Bayes nets and someone recommended using indices into a Vec
as pseudo-references, but I thought that was hideous, so I ended up using Rc
and RefCell
sort of like in Nick Cameron's tutorial.)
There were a couple of pre-conf community events scheduled for Friday night: a Chef/Habitat meetup, and a hack night for the new Tokio async IO project. I decided to only go to the hack night and wander around downtown Portland for the few hours after the lifetimes session and before the hack night. Some people were protesting prison labor practices at an AT&T store. On a whim, I visited the famous Powell's City of Books—it's very large and has rooms mostly named after colors!—the math section is in the Pearl room! On a further whim, I bought a book! You can't prove that the book isn't completely unrelated to the world-shattering insight that has been eating my life for two months! Then it was time to hack on Tokio ("... I'm on my way; in my brand new auto, it's not so far away").
So, I don't really understand Tokio. I think it's supposed to provide a new, better high-level async IO story for Rust? After procuring some help from the knowledgable hackers around me ("Cargo sucks! ... just getting your attention"), I was at least able to run some example code (there were some dependency problems), and I submitted a pull request suggesting that the appropriate cargo run --example
command be mentioned in the README. Out of despair and determination to get something nontrivial done at hack night, I submitted a pull request for vector iteration to rulinalg, the linear-algebra library that I was already familiar with from having contributed to its code before it got pulled out of rusty-machine. (My pull request has some lifetime annotations in it, but even after the afternoon's training, I still felt like I was just imitating examples and slapping tick-a on things.)
The next day was the actual conference! I got to meet my hero Julia Evans and she gave me a paper (!) zine.
Matsakis and Aaron Turon gave the opening keynote on the remarkable year Rust has had: 175 new features have been stablized, and we have more exciting new features on nightly, like specialization, impl Trait
, ?
, custom derive, and (now shining brightly in orbit) MIR.
There were some technical difficulties getting the projectors hooked up to the laptop for the next talk. Steve Klabnik said that the break was brought to us by Apple, who is not sponsoring us, but really wants us to have a long break between talks. "They should have written it in Rust!" I shouted (as if someone had to say it); Steve shrugged.
The projector issue got fixed and Liz Baille, developer at Tilde and graphic-novelist, gave a really funny talk in the form of an "illustrated adventure guide" to Rust ("You might have noticed how clean and beautiful Rustlandia is, and you might have also noticed that there are no garbage cans anywhere").
Geoffroy Couprie spoke about getting Rust code into the VLC media player: rewriting existing C code is hard, he says, but it's doable in Rust today.
Suchin Gururangan and Colin O'Brien spoke about a machine-learning classifier that they built to detect posts about the Rust video game that were erroneously posted to our /r/rust subreddit instead of /r/PlayRust. ("Ability to copy/duplicate maps" was a cute example of a post title the classifier wasn't very confident about—maps could be game maps or hashmaps!)
Without Boats is working on notty, a new terminal that aims to improve on the reigning standards (much of which ossified in the days of line printers) with better Unicode support and the ability to display images. Boats says that notty uses many more traits than is usual for the Rust ecosystem. For example, consider a write
method for writing something into a terminal's grid, which could either be a normal character, an extra-wide character, a combining modifier character, or an image. The original, intuitive solution was for write
to take an enum (with variants Char
, WideChar
, CharModifier
, and Image
) that matches on the thing being written, but that was problematic because the method quickly became enormous, and each kind of data had to take a &mut
reference to the struct representing the entire grid. Whereas after a refactoring, there is instead a Writer
trait that gets implemented each type of writable thing. Boats followed up with a case study about separating a terminal into panels ("Let's say you're using a text editor like Vim, or the other one") and finished with an exhortation to "MAKE RUST TRAIT AGAIN".
Alex Crichton gave a talk titled "Back to the Fututes". Maybe I don't have much to say about this one for the same reason I didn't get much done at the Tokio hack night??
Raph Levien gave a talk about Xi, a modern editor dedicated to being performant (as operationalized by never blocking). It uses ropes and the fact that strings under concatenation are a monoid. Levien says that regex-based highlighting is not the future because actual lexing is faster, and that people should stop sneering at him for using JSON-RPC, which is really well-optimized and readily available ("You talk about batteries included; this is a AA battery, not a CR123A").
Josh Triplett gave a talk about what he learned about the Rust RFC process in the course of adding untagged unions (and the hope that they provide) to the language. There was a bit of feedback from the sound systems as Triplett began to speak about the application that motivated his interest in Rust: virtual machines, which are used for containment and isolation ("which would be useful in audio systems as well"—laughter from the audience). Buffer overflows were written about in 1972, first exploited in the wild in 1988, and we're still talking about them in 2016. Rust is interesting because it's actually a credible replacement for C++. Tagged unions (we call them enums) are ubiquitous in Rust, but it's useful to have untagged unions for interfacing with C code that uses them; currently, you need an unsafe
block and nasty things like mem::transmute to deal with C unions, and we'd prefer to have a safe construct for this. Unfortunately, Rust's backwards-compatibility guarantees mean we can't strictly use union
as a keyword, because there could be existing code that uses it as a variable binding. But it turns out that it's possible to make the parser smart enough to recognize union
as a "contextual keyword": we can use it like a keyword because its position in the syntax tree is sufficient to distinguish it from union
being used as a variable. The discussion threads on RFCs can get kind of unwieldy, so people make summary posts that describe the state of the debate so far. Implementation isn't part of the RFC process (although it can happen in parallel, with the understanding that the RFC can change); a tracking issue for implementation is opened when the RFC is approved. Untagged unions are now available in Nightly Rust behind a feature flag!
Finally, Julia Evans gave a talk on "Learning Systems Programming With Rust" and how Rust makes improbable programs possible (you could write correct C, but you won't).
And that was RustConf! The next day, since my flight wasn't until evening, I also walked across some kind of bridge and visited PDX Maker Faire since I happened to be in town and someone I met on the internet was there to show off this duplicate of a set piece from the Stranger Things television series that they made with an Arduino.
My flight back to SFO was delayed a few hours due to some sort of technical difficulties in Chicago. As I sat waiting at the gate, beginning to draft this post and trying not to let my soul be consumed by the world-shattering abyss induced by cruel apprehension of patterns that innocents were not meant to see, I felt a deep sense of gratitude that I should have the privilege to participate in such a brilliant, welcoming community as that which surrounds the Rust programming language and its mission to bring systems programming to the masses in this 21st century!
what's a good primer on why Rust is interesting / why I should learn Rust