Without boats, dreams dry up A bit over a year ago, I wrote some notes on a “smaller Rust” - a higher level language
that would take inspiration from some of Rust’s type system innovations, but would be simpler by
virtue of targeting a domain with less stringent requirements for user control and performance.
During my time of unemployment this year, I worked on sketching out what a language like that would
look like in a bit more detail. I wanted to write a bit about what new conclusions I’ve come to
during that time. Today I made a new release of the iou library, which contains idiomatic Rust bindings to the
liburing library. This library allows users to manipulate the new io-uring
interface for asynchronous IO on Linux. For more context, you can read my previous post on the
first release of iou last year. This new release greatly expands the API of iou, introduces some valuable improvements, and contains
some breakages. I figured I would let this blog post serve as some basic release notes. I’ve just released a new crate called propane, which is a library for writing generator functions. It can only run on nightly: It’s hard to believe that its been more than 3 years since I opened RFC 2000, which defined
the const generics for Rust. At the same time, reading the RFC thread, there’s also been a huge
amount of change in this area: for one thing, at the time the RFC was written, const fns weren’t
stable, and consts weren’t even being evaluated using miri yet. There’s been a lot of work over the
years on the const generics feature, but still nothing has shipped. However, I think we have defined
a very useful subset of const generics which is stable enough to ship in the near term. Last time I wrote about ringbahn, a safe API for using io-uring from Rust.
I wrote that I would soon write a series of posts about the mechanism that makes ringbahn work. In
the first post in that series, I want to look at the core state machine of ringbahn which makes it
memory safe. The key types involved are the Ring and Completion types. While implementing ringbahn, I introduced at least two bugs that caused memory safety
errors, resulting in segfaults, allocator aborts, and bizarre undefined behavior. I’ve fixed both
bugs that I could find, and now I have no evidence that there are more memory safety issues in the
current codebase (though that doesn’t mean there aren’t, of course). I wanted to write about both of
these bugs, because they had an interesting thing in common: they were both caused by destructors. This is just a note on getting the best performance out of an async program. The point of using async IO over blocking IO is that it gives the user program more control over
handling IO, on the premise that the user program can use resources more effectively than the kernel
can. In part, this is because of the inherent cost of context switching between the userspace and
the kernel, but in part it is also because the user program can be written with more specific
understanding of its exact requirements. In my previous post, I discussed the new io-uring interface for Linux, and how to create
a safe API for using io-uring from Rust. In the time since that post, I have implemented a prototype
of such an API. The crate is called ringbahn, and it is intended to enable users to
perform IO on io-uring without any risk of memory unsafety. Last fall I was working on a library to make a safe API for driving futures on top of an an io-uring
instance. Though I released bindings to liburing called iou, the futures integration, called
ostkreuz, was never released. I don’t know if I will pick this work up again in the future but
several different people have started writing other libraries with similar goals, so I wanted to
write up some notes on what I learned working with io-uring and Rust’s futures model. This post
assumes some level of familiarity with the io-uring API. A high level overview is provided in this
document. In a previous post, I shortly discussed the concept of “effects” and the parallels
between them. In an unrelated post since then, Yosh Wuyts writes about the
problem of trying to write fallible code inside of an iterator adapter that doesn’t support it. In a
previous discussion, the users of the Rust Internals forum hotly discuss the notion of
closures which would maintain the so-called “Tennant’s Correspondence Principle” - that is,
closures which support breaking to scopes outside of the closure, inside of the function they are in
(you can think of this is closures capturing their control flow environment in addition to capturing
variables). I think it may not be obvious, but these discussions are all deeply related. They all arise from
what is, in my opinion, one of the biggest problems with the design of the Rust language: its
failure at 1.0 to give good support for handling common effects related to program control flow.Revisiting a 'smaller Rust'
iou version 0.3 released
Propane: an experimental generator syntax for Rust
#![feature(generators, generator_trait, try_trait)]
#[propane::generator]
fn fizz_buzz() -> String {
for x in 1..101 {
match (x % 3 == 0, x % 5 == 0) {
(true, true) => yield String::from("FizzBuzz"),
(true, false) => yield String::from("Fizz"),
(false, true) => yield String::from("Buzz"),
(..) => yield x.to_string(),
}
}
}
Shipping Const Generics in 2020
Ringbahn II: the central state machine
Two Memory Bugs From Ringbahn
Futures and Segmented Stacks
Ringbahn: a safe, ergonomic API for io-uring in Rust
Notes on io-uring
The problem of effects in Rust