Changing the rules of Rust


In Rust, there are certain API decisions about what is and isn’t sound that impact all Rust code. That is, a decision was made to allow or not allow types which have certain safety requirements, and now all users are committed to that decision. They can’t just use a different API with different rules: all APIs must conform to these rules.

These rules are determined through certain “marker” traits. If a safe API could do something to a value of a type which some types don’t support, the API must be bound by that marker trait, so that users can not pass values of those types which don’t support that behavior to that API. In contrast, if Rust allows APIs to perform that behavior on any type, without any sort of marker trait bound, then types which don’t support that behavior cannot exist.

I’m going to give three examples to show what I mean, each of which Rust has considered at different points, though only the first one actually exists in Rust.

A governance system, if you can keep it


One of the most famous anecdotes that forms the basis of the United States’ political self-identity is the story of an interaction between Benjamin Franklin and Elizabeth Willing Powel after the Constitutional Convention of 1787, which established the United States’ present form of government. Powel asked Franklin what sort of government the U.S. was to have, to which he replied: “a republic, if you can keep it.”

Given the self-conscious references to “constitutions” and “checks and balances” in the Rust project’s recent governance RFC and the discourse around it, some further reflection on this quote and its implications about governance as such might now be appropriate for the project and its community.

Iterator, Generator


I have been devoting a lot of my free time in the past month to thinking about structured concurrency, and a blog post about that is coming soon, but first I want to revisit iterators and generators.

In a previous post, I wrote about one of the hardest problems for generators: self-referential generators. Unlike the Future trait when we were designing async functions, the Iterator trait is already stable, and it does not take a pinned reference to itself. This means an Iterator cannot be self-referential.

The Scoped Task trilemma


This is the first post in a series of posts about concurrency in Rust, and the different APIs that can exist to support it. Unlike my recent series on control-flow effects, this series isn’t driving toward any particular vision of what I think the Rust project should do. Instead, I am just trying to publicly explore the problem space and build tools for thinking about the issues involved. I’m not sure what the “right” concurrency API is.

Generators


One of the main emphases of my recent posts has been that I believe shipping generators would solve a lot of user problems by making it easy to write imperative iterative code, and especially to make that iterative code interact well with asynchrony and fallibility as well. One thing that frustrates me about the situation is that generators have been nearly ready to ship for years now, but very little visible progress has been made. In particular, the core compiler transform to take a generator and produce a state machine already exists, because it’s exactly how async functions are implemented.

The AsyncIterator interface


In a previous post, I established the notion of “registers” - code in Rust can be written in different registers, and it’s important to adequately support all registers. I specifically discussed the low-level interface of the AsyncIterator trait, about which there is currently a debate. The interface it currently has is a method called poll_next, which is a “poll” method like Future::poll. Poll methods are very “low-level” and are harder to write correctly than async functions. Some people would like to see AsyncIterator shifted to have an async next method, simply the “asyncified” Iterator trait.

Const as an auto trait


The previous two posts in this series tried to discuss the design of Rust through the lens of some higher level language concepts:

  • First, the notion that programming languages have different registers
  • Second, the idea that not all patterns should be made into abstractions

This post does not introduce any such high-minded concept. It is entirely down in the weeds. That’s partly because it is based on content I removed from the previous post so that post could focus on the higher point.

Patterns & Abstractions


This is the second post in an informal series commenting on the design of async Rust in 2023. In my previous post, after a discussion of the “registers” in which control-flow effects could be handled in Rust, I promised to turn my attention to recent proposals around a concept called “keyword generics.” For reference, there are two posts by the current design team of Rust that are my reference point for this commentary:

I’m not going to reiterate these blog posts at length, but in brief “keyword generics” is a proposal to introduce a new kind of abstraction to Rust, to allow types to be abstracted over certain effects. The examples have focused on async and const as the effects, but there is sometimes discussion of try as well. Astute readers will notice this is an overlapping but not identical set of effects to the effects I identified in my last post; I did not mention const as an effect, and as far as I know the keyword generics working group has not devoted much or any time to considering iteration as an effect.

The registers of Rust


It’s been nearly two and half years since I was an active contributor to the Rust project. There are some releases that I’ve been very excited about since then, and I’m heartened by Niko’s recent blog post emphasizing stability and polish over grand new projects. But I’ve also felt a certain apprehension at a lot of the directions the project has taken, which has often occupied my thoughts. From that preoccupation this blog post has emerged, hopefully the first in a series over the next few weeks outlining my thoughts on the design of Rust in 2023, especially in connection to async, and I hope its impact will be chiefly positive.

Ringbahn III: A deeper dive into drivers


In the previous post in this series, I wrote about how the core state machine of ringbahn is implemented. In this post I want to talk about another central concept in ringbahn: “drivers”, external libraries which determine how ringbahn schedules IO operations over an io-uring instance.