W
without.boats
without.boats · Personal blog · on HN since 2020
Withoutboats, a Rust language contributor, writes about Rust language design, async, pinning and governance.
What HN saysCommenters recommend it as a source for Rust fundamentals and language design, especially the posts on a smaller Rust and async deadlocks.
80HN people shared it
54linked it in comments
59stories submitted
1.5Kstory points
3 months agolast shared
2020people per year2026
Most-shared posts
- 1Revisiting a 'Smaller Rust'16 people · 18 points · first shared 2020 · 3 comments on HN
- 2Notes on a Smaller Rust (2019)13 people · 11 points · first shared 2020 · 5 comments on HN
- 3Why Async Rust?10 people · 2 points · first shared 2023 · discussion on HN
- 4The Scoped Task Trilemma8 people · 2 points · first shared 2023 · discussion on HN
- 5Pinned Places6 people · 52 points · first shared 2024 · 6 comments on HN
- 6The Registers of Rust5 people · 6 points · first shared 2023 · discussion on HN
- 7A governance system, if you can keep it5 people · 3 points · first shared 2023 · discussion on HN
- 8References are like jumps4 people · 98 points · first shared 2024 · 10 comments on HN
- 9Asynchronous clean-up4 people · 95 points · first shared 2024 · 70 comments on HN
- 10Pin3 people · 502 points · first shared 2024 · 120 comments on HN
- 11Coroutines and effects3 people · 280 points · first shared 2024 · 97 comments on HN
- 12Let futures be futures3 people · 116 points · first shared 2024 · 96 comments on HN
- 13FuturesUnordered and the order of futures3 people · 43 points · first shared 2024 · 16 comments on HN
- 14Changing the Rules of Rust3 people · 22 points · first shared 2023 · 2 comments on HN
- 15Ownership3 people · 7 points · first shared 2024 · discussion on HN
What HN people wrote when they linked it
Dagonfly · 2025-10-31
That's a really subtle version of the deadlock described in withoutboats FuturesUnordered post [0]
When using “intra-task” concurrency, you really have to ensure that none of the futures are starving.
Spawning task should probably be the default. For timeouts use tokio::select! but make sure all pending futures are owned by it. I would never recommend FuturesUnordered unless you really test all edge-cases.
[0] https://without.boats/blog/futures-unordered/
m11a · 2025-08-30
For Rust, there are a few blogs that lean more on fundamentals and language design (like https://without.boats/blog, https://smallcultfollowing.com/babysteps).
For misc Rust engineering, like the OP, I agree it’s quite scattered. I personally like to save good ones in my Feedbin as I encounter them
yccs27 · 2025-07-08
I can understand why Rust didn't implement an IO monad (even though I'd love it), but linear types seem like they would fit right in with the rest of Rust. Not sure why they didn't include them.
There's actually two different ways that Rust types can always be discarded: mem::forget and mem::drop. Making mem::forget unsafe for some types would be very useful, but difficult to implement without breaking existing code [1].
Btw, you are correct with linear types - Affine types allow discarding (like Rust already does).
[1] https://without.boats/blog/changing-the-rules-of-rust/
cogman10 · 2024-12-26
Here's something not contrived that does pretty frequently come up [1]
The gist of it is even though tasks can be somewhat guaranteed to live only for the scope of the current method, you can't guarantee that with the rust type system. The end result is needing to copy immutable data or use ARC needlessly.
It's annoying enough that the language added "scoped_threads" which work great, but can't be translated into rust async tasks.
[1] https://without.boats/blog/the-scoped-task-trilemma/
wezm · 2024-11-19
Might be worth giving these posts a read when thinking about a lightweight Rust:
- https://without.boats/blog/notes-on-a-smaller-rust/ - https://without.boats/blog/revisiting-a-smaller-rust/
It's also niche, but https://gleam.run/ might be a candidate alternate language, depending on your use-case.
sam_bristow · 2024-02-26
jkarneges · 2024-02-04
> rust async seems to be a terrible design blunder [...] that could destroy the community and render the language worthless
What an extreme take!
Async Rust as-it-is makes perfect sense for Rust's primary audience: folks who desire control/performance enough that they would otherwise be using C/C++. To call async an "efficiency hack" is to underestimate how important that efficiency is to Rust's existence. If async didn't work the way it does, it wouldn't have gained traction within the Rust community (folks would have just ignored it and kept writing state machines by hand) and it wouldn't have been able to act as a draw for C/C++ developers. It's a killer feature, and has enabled the language to thrive.
Not only is it a killer feature, the way it is designed is the only way it could have been. See: https://without.boats/blog/why-async-rust/
bryanlarsen · 2024-01-24
Good question. I might have been projecting my hopes to make it sound more formal, but the only thing I could find following my cookie crumbs was the mention in https://without.boats/blog/why-async-rust/
dogleash · 2023-05-31
Maybe it can't be solved behind closed doors because that's where the problems are? Rust's governance has always been a secret backroom and reliance on soft power to make the real decisions.
I don't know how well documented it is, but there are at least two posts acknowledging it https://without.boats/blog/if-you-can-keep-it/ https://fasterthanli.me/articles/rust-the-wrong-people-are-resigning
If this could be fixed quietly, I expect it would have been. But it looks like they had just finished using the back channels to make sure nobody gets the impression rust core thought ThePHD's research was worth perusing.
nerpderp82 · 2023-03-09
Article is worth a read, isn't referring to musical registers.
https://without.boats/blog/the-registers-of-rust/
https://en.wikipedia.org/wiki/Register_(sociolinguistics)
pcwalton · 2021-03-11
How does this proposal address the problems with "blocking on drop does not work" here? https://without.boats/blog/io-uring/
The problem here is not with Rust's async design. It's that Rust has affine types and not linear types. This is not something that could have been solved with more work on the design. It is not that there was "a decision" to allow dropping tasks; it's a constraint on the design that the language requires. (Personally, I'm unsure as to whether a practical language with true linear types is possible, but it's worth experimenting with. Rust is not and never will be that language, however.)
steveklabnik · 2020-08-11
This is a complicated question, and ends up different for each individual. For me, I don't see the borrow checker as being more silly than GC, just an alternative, and one that speeds up my development process, not slows it down. I am also, of course, incredibly biased.
Rust also has many, many features that are not the borrow checker. Some people prefer Rust because of those features, in spite of the borrow checker.
https://without.boats/blog/notes-on-a-smaller-rust/ is also one of my favorite bits of writing on this topic.
zozbot234 · 2020-07-05
AIUI, withoutboats has described this issue in depth in https://without.boats/blog/io-uring/ and follow-on blog posts, which also go into the design of ringbahn itself.
kibwen · 2025-12-26
> Why isn’t there a variant of &mut that doesn’t allow swapping the value?
This is a very insightful observation, and Niko Matsakis (leading influence of Rust's borrow checker) would likely agree with you that this is an instance where Rust's default borrowing rules are probably too permissive, in the sense that being more restrictive by default regarding the "swappability" of &mut could lead to Rust being able to provide more interesting static guarantees. See his blog post here: https://smallcultfollowing.com/babysteps/blog/2024/09/26/overwrite-tra…
> Why is pinning a weird sticky property of a reference? Shouldn’t non-movability of an object be a property of the object’s type?
See this blog post from withoutboats: https://without.boats/blog/pinned-places/ for arguments as to why pinning is properly modeled as a property of a place rather than a type (particularly the section "Comparison to immovable types"), as well as this post from Niko that ties this point in with the above point regarding swappability: https://smallcultfollowing.com/babysteps/blog/2024/10/14/overwrite-and…
raggi · 2024-09-09
https://github.com/tailscale/tailscale/pull/2370 was a practical drive toward this, will not proceed on this path.
much more approachable, boats has written about challenges integrating in rust: https://without.boats/tags/io-uring/
in the most general form: you need a fairly "loose" memory model to integrate the "best" (performance wise) parts, and the "best" (ease of use/forward looking safety) way to integrate requires C library linkage. This is troublesome in most GC languages, and many managed runtimes. There's also the issue that uring being non-portable means that the things it suggests you must do (such as say pinning a buffer pool and making APIs like read not immediate caller allocates) requires a substantially separate API for this platform than for others, or at least substantial reworks over all the existing POSIX modeled APIs - thus back to what I said originally, we need a replacement for POSIX & BSD here, broadly applied.
Voultapher · 2023-06-23
> Tracing GC is the simplest model for the user, and helps with time management and development velocity, two very important aspects of software engineering.
> Borrow checking is very fast, and helps avoid data races.
One thing many people seem to assume is that not having to care about memory means you can program faster and get to your goal faster. As the author here seems to do. However as it turns out, if your program is more complex than a ~100-1000 lines of codes, explaining in a explicit way who owns what and who gets to change state when is a very useful way to avoid bugs.
Saoirse Shipwreckt aka withoutboats mentioned this a while ago in https://without.boats/hire-me/
> Rust works because it enables users to write in an imperative programming style, which is the mainstream style of programming that most users are familiar with, while avoiding to an impressive degree the kinds of bugs that imperative programming is notorious for. As I said once, pure functional programming is an ingenious trick to show you can code without mutation, but Rust is an even cleverer trick to show you can just have mutation.
and later follows up on this in https://without.boats/blog/revisiting-a-smaller-rust/
> I still think this is Rust’s “secret sauce” and it does mean what I said: the language would have to have ownership and borrowing. But what I’ve realized since is that there’s a very important distinction between the cases in which users want these semantics and the cases where…
pornel · 2021-11-13
withoutboats has explored these trade-offs on their blog, e.g. https://without.boats/blog/poll-drop/ and as part of io-uring investigation. io-uring is the main use-case for async drop, but even that has been mostly solved without language changes: there is tokio-uring now.
Apart from requiring a safe zero-copy io-uring abstraction to use a buffer pool instead of "bare" references, Rust's polling+cancellation model is fantastic. It's incredibly convenient to be able to externally time out and abort any Future, without needing to pass contexts/tokens and/or needing every I/O operation to have an explicit timeout option.
It's annoying when conscious small trade-offs are framed as fatal flaws (like the title of TFA). In the case of the cancellation model the "doesn't work" is that a safe abstraction is not entirely zero-cost. But when you use async in JS, C#, or Golang, you already have unavoidable heap allocations and memcpys (and another set of difficulties with cancellation). Rust's worst-case flaw is comparable to a normal mode of operation in a few other async languages. The scale of the issue is overblown, and its benefits are overlooked.
withoutboats2 · 2021-03-10
This post is completely and totally wrong. At least you got to ruin my day, I hope that's a consolation prize for you.
There is NO meaningful connection between the completion vs polling futures model and the epoll vs io-uring IO models. comex's comments regarding this fact are mostly accurate. The polling model that Rust chose is the only approach that has been able to achieve single allocation state machines in Rust. It was 100% the right choice.
After designing async/await, I went on to investigate io-uring and how it would be integrated into Rust's system. I have a whole blog series about it on my website: https://without.boats/tags/io-uring/. I assure you, the problems it present are not related to Rust's polling model AT ALL. They arise from the limits of Rust's borrow system to describe dynamic loans across the syscall boundary (i.e. that it cannot describe this). A completion model would not have made it possible to pass a lifetime-bound reference into the kernel and guarantee no aliasing. But all of them have fine solutions building on work that already exists.
Pin is not a hack any more than Box is. It is the only way to fit the desired ownership expression into the language that already exists, squaring these requirements with other desireable primitives we had already committed to shared ownership pointers, mem::swap, etc. It is simply FUD - frankly, a lie - to say that it will block "noalias," following that link shows Niko and Ralf having a fruitful…
michalsustr · 2026-02-18
Very interesting. What is the runtime model, based on the "async trilema" https://without.boats/blog/the-scoped-task-trilemma/ ?
I assume tokio-like, i.e. work-stealing?
ViewTrick1002 · 2025-12-05
Almost the entire early large scale adoption came from people writing async web services.
From 2023:
> In that regard, async/await has been phenomenally successful. Many of the most prominent sponsors of the Rust Foundation, especially those who pay developers, depend on async/await to write high performance network services in Rust as one of their primary use cases that justify their funding.
https://without.boats/blog/why-async-rust/
nemothekid · 2025-11-12
While I think the problem highlighted in the article is a longstanding problem for Rust[1], I don't think the example, or finalizers was the problem with Futurelock as described by Oxide.
I'm not sure you can write a simple example in Python, because Rust's future's architecture and Python's is different. `futurelock` is an issue of cancellation safety which is a stranger concept (related to finalizers, but not in the way OP has described).
Personally, I think `tokio::select!` is dangerous and I don't use it my code - it's very easy to deadlock yourself or create weird performance issues. I think the interface is too close to Go and if you don't understand what is going on, you can create deadlocks. That said, even if you avoid `tokio::select!`, I think cancellation safety is one of those dragons that exist in async rust.
[1] https://without.boats/blog/poll-drop/
sunshowers · 2025-10-15
Hi -- I'm the one who presented the talk -- honored!
I'm curious how you got to "async Rust needs a [tracing] garbage collector" in particular. While it's true that a lot of the issues here are downstream of futures being passive (which in turn is downstream of wanting async to work on embedded), I'm not sure active futures need a tracing GC. Seems to me like Arc or even a borrow-based approach would work, as long as you can guarantee that the future is dropped before the scope exits (which admittedly isn't possible in safe Rust today [0]).
[0]: https://without.boats/blog/the-scoped-task-trilemma/
saviorand · 2025-09-08
Good point! You might be interested in reading this article on the topic: https://without.boats/blog/coroutines-and-effects/
Expurple · 2025-07-16
The deal with strict Rust-style references is that they solve mutable aliasing bugs (data races, iterator invalidation, all kinds of unexpected mutation really). That's the deal with Rust.
The whole "memory management" thing is mostly a historical accident. We could have a "smaller Rust" that auto-boxes values, has a runtime that handles reference cycles for you, and doesn't guarantee anything about the stack vs heap: https://without.boats/blog/notes-on-a-smaller-rust/
ogoffart · 2025-05-20
Off-topic, but the idea of "colored functions" from the "What Color is Your Function" article doesn't apply to Rust's async/await. That article is about JavaScript before it had async/await, when it used callbacks.
In Rust, you can call async functions from normal ones by spawning them on the executor. The .await syntax isn't as painful as dealing with callbacks and closures in JavaScript. Plus, if you call an async function incorrectly, Rust's compiler will catch it and give you a clear error message, unlike JavaScript, where bad function calls could lead to unpredictable behavior. The premises of the article don't apply, so Rust's async/await doesn't introduce the same "colored function" issues.
(See also https://without.boats/blog/let-futures-be-futures/ )
NobodyNada · 2025-05-14
This might not be exactly what you're looking for, but I really like "References are like jumps": https://without.boats/blog/references-are-like-jumps/
iknowstuff · 2025-02-21
Jesus fucking christ not this again.
https://without.boats/blog/why-async-rust/
There are entire articles explaining precisely why. Your lack of knowledge contributes very little to an article about a framework.
smatija · 2024-10-30
This reference at the bottom of article was very interesting to me: https://without.boats/blog/io-uring/
"So I think this is the solution we should all adopt and move forward with: io-uring controls the buffers, the fastest interfaces on io-uring are the buffered interfaces, the unbuffered interfaces make an extra copy. We can stop being mired in trying to force the language to do something impossible. But there are still many many interesting questions ahead."
Philpax · 2024-10-07
You may be interested in https://without.boats/blog/the-registers-of-rust/ :)
withoutboats3 · 2024-09-27
My blog is located at https://without.boats
Here are some relevant posts:
https://without.boats/blog/a-four-year-plan/
https://without.boats/blog/poll-next/
https://without.boats/blog/asynchronous-clean-up/
https://without.boats/blog/pinned-places/