Taylor Cramer (tmandry) writes about Rust, focusing on async/await, compiler internals, language design proposals and Bazel builds.
HN commenters cite it for Rust language design, such as the contexts and capabilities proposal and the explanation of buffered stream polling.
> I like Rust's pragmatism.
Me too.
But Scala is also a very pragmatic language. If you want something academic go for Haskell.
> Allow limited operator overload.
Nitpick: Scala does not have any operators. So it doesn't have operator overloading at all.
Scala simulates operators by infix method syntax.
Instead of writing `1.+(2)` you can just write `1 + 2`. But the later is the same method call as the first one!
> Eschew HKT for a simpler abstractions.
AFAIK HKTs are more or less "just postponed" in Rust, AFAIK.
People would like to add them of course. The discussion goes on forever by now. Some small insight (there is much more when you look for it):
https://github.com/rust-lang/rfcs/issues/324
https://internals.rust-lang.org/t/higher-kinded-types-the-difference-between-giving-up-and-moving-forward/3908/7
> Don't go in the deep end with type power, nor too much in opposite direction and avoid any complicated feature.
While having a full ML style type system with afine types on top, and quite some other type level mechanics up to singleton types?
Sure sure, no power in here. :-)
> The more powerful feature the more abusable it is, and Scala loves the power at all cost.
Everything is "abusable". This is not an argument.
But that Scala loves power at all cost is simply not true. The contrary is.
Just to cite one of the most influential post in Scala land of all times:
https://www.lihaoyi.com/post/StrategicScalaStylePrincipleofLeastPower.html
This, and the…
Nice, love to see posts where people give their experience like this. It's a rare thing.
> the Cargo compiler
Really small thing, cargo is a build tool, not a compiler. `rustc` is the main compiler, cargo wraps that and provides helper utilities for stuff like package/ project management.
> Detecting an out-of-bounds access at compile time requires a deeper analysis of the code that would significantly slow down compilation time (which is already too slow IMO).
The compiler actually is likely doing this stuff already because it's trying to optimize away any branches. I bet it already can see in these basic examples that the panic is unconditional. There are crates that leverage this fact, like `no-panic`, to try to see if your code is panicking.
Also worth noting that "panic" is kinda like a runtime exception in Java and it can be caught. In fact it's automatically caught at the thread boundary - that's going to just be the main thread in your example.
> This section is about a recent behavior I observed where our team woke up to one of our crates’ (dependencies’) dependencies starting to panic in production under specific conditions
Bummer! Especially for a crate as popular as reqwests. My suggestion would be to assume any code can panic and build around that. That means a few things.
1. It means to test your code. Integration tests are great for catching bugs like this, even a basic smoke test would likely catch it.
2. Use a control plane that will automatically…
The poll_progress post linked above explains the situation. When polling the overall stream, you alternate between awaiting in the buffered interface or in the subsequent adapters. This is because the different futures are not peers with regard to the executor, but there's a chain of futures and `FilterMap` only calls `poll` on its parent when it's done with the current item.
This post was also helpful to understand the issue: https://tmandry.gitlab.io/blog/posts/for-await-buffered-streams/
There's been some side work towards defining dynamic scoping for rust. E.g., allow functions to declare a dependency on some value or type, and then require callers to either pass that dependency explicitly, or have it on their dependencies.
One of the proposed uses was for allocators.
https://tmandry.gitlab.io/blog/posts/2021-12-21-context-capabilities/ is one of the more cogent proposals.
I don't think any of these has made substantial progreess towards being approved though.
Most Rust ORMs and query builders expose a transaction API that takes a closure and runs it inside the transaction, rolling back on unwind or (in most cases) if it's not explicitly committed. This is the most common idiom in Rust for dealing with situations where you want to pass extra data to or from a cleanup routine. Unfortunately, for the async use case in particular it happens to be unsound: https://tmandry.gitlab.io/blog/posts/2023-03-01-scoped-tasks/
This is actually a serious proposal for the Rust language, it's called contexts and capabilities
Here's a blog post from 2021 https://tmandry.gitlab.io/blog/posts/2021-12-21-context-capabilities/
This isn't implemented yet, and the proposal never matured into a proper RFC, but there's high interest for some solution along those lines