Without boats, dreams dry up I want to address a controversy that has gripped the Rust community for the past year or so: the
choice by the prominent async “runtimes” to default to multi-threaded executors that perform
work-stealing to balance work dynamically among their many tasks. Some Rust users are
unhappy with this decision, so unhappy that they use language I would characterize as
melodramatic: The Original Sin of Rust async programming is making it multi-threaded by default. If premature
optimization is the root of all evil, this is the mother of all premature optimizations, and it
curses all your code with the unholy It’s always off-putting to me that claims written this way can be taken seriously as a technical
criticism, but our industry is rather unserious. I want to wrap up my consideration of the idea of adding new auto traits to Rust with some notes
from a conversation I had with Ariel Ben-Yehuda. You can read these two previous posts for context: In my previous post, I described the idea of using an edition mechanism to introduce a new
auto trait. I wrote that the compiler would need to create an “unbreakable firewall” to prevent
using The response has been pretty optimistic that ensuring this would be possible, even though I wrote in
the post myself that I “despair” over how difficult it was. I’ve received a great example from Ariel
Ben-Yehuda which demonstrates how this problem is more difficult to solve than you would probably
think. 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. 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. 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. 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. 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. 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 The previous two posts in this series tried to discuss the design of Rust through the lens of some
higher level language concepts: 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.Thread-per-core
Send + 'static
, or worse yet Send + Sync + 'static
, which
just kills all the joy of actually writing Rust.Generic trait methods and new auto traits
Follow up to "Changing the rules of Rust"
!Leak
types from the new edition with code from the old edition that assumes values of all
types can be leaked.Changing the rules of Rust
A governance system, if you can keep it
Iterator, Generator
The Scoped Task trilemma
Generators
The AsyncIterator interface
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