boats (withoutboats) is a Rust contributor who writes about language design, async/await, effects, error handling, and io-uring.
HN commenters recommend his posts, such as Zero Cost Abstractions and the explicitness essay, and cite his work when discussing Rust design choices and error crates.
Rust generics can be written in a more compact way,
fn f(x: impl Test) { ... }
Is basically the same as
fn f<T: Test>(x: T) { ... }
Having to annotate the return value of functions is partly to make sure don't break semver accidentally, but it also constrains type inference to a local search rather than global search, avoiding spooky action at distance (when modifying a function changes the signature of another function). It seems that semver hazards are more important in languages with static types.
That pub? It's punching waaay above its weight! Having things private by default protects against semver hazards too, but it's also the major tool that enable writing safe code that uses unsafe { .. } blocks underneath. That's because a single unsafe block typically taints the whole module it's contained (such that even safe code needs a safety review). Being able to write safe abstractions of unsafe code is the pull of Rust so people need to get it right.
For example, setting the field len of a Vec to a value larger than what's initialized in its buffer will cause UB when accessing that memory; if this field weren't private, making a safe Vec would be impossible (likewise, a function that could set the len to any value must be private, etc).
So pub can only be used for things that are safe to export, and people should be careful with that.
Here's my least favorite syntax from there: that damn Ok(..). Ok-wrapping sucks. There were proposals to remove it, but they…
The language team member you are criticizing for exploring what Ok-wrapping could look like goes into your particular argument in quite some detail here: https://boats.gitlab.io/blog/post/2017-12-27-things-explicit-is-not/
Your general concern, which is that "new syntax makes things less explicit, and is therefore bad", isn't logical.
Adding new syntax has many costs (not only one), but it can also have benefits (hence why it is being proposed), and the real question is whether the benefits outweigh the costs.
That is, whether it is a trade-off worth making.
In the blog post, the author quantify the actual costs of not having Ok-wrapping on their own code, as well as the costs of adding Ok-wrapping to the language, and for them, their numbers suggest that the trade-off is worth it - so they went on and implement it as a proc macro, and are using it on their own code. They don't care whether anybody elses uses it, and they don't have to.
You could have disagreed with their quantification, maybe you have different numbers, and that leads to a different outcome of whether the trade-off is worth making or not.
Instead, you just disagree, without making an argument, and others are doing the same.
In my eyes, this kind of hurts your position, since it gives me the impression that those against Ok-wrapping can't really argue why. Whether they just can't argue in general, lack data to back their arguments up, or maybe even whether they are Rust users at all, is anybody's…
>Also, zero-cost abstraction is likely the biggest bullshit I've ever heard, there is a lot of cost and there's also a lot of waiting for compiler if you're using cargo packages.
"(...) there are two factors that make something a proper zero cost abstraction:
No global costs: A zero cost abstraction ought not to negatively impact the performance of programs that don’t use it. For example, it can’t require every program carry a heavy language runtime to benefit the only programs that use the feature.
Optimal performance: A zero cost abstractoin ought to compile to the best implementation of the solution that someone would have written with the lower level primitives. It can’t introduce additional costs that could be avoided without the abstraction."
https://boats.gitlab.io/blog/post/zero-cost-abstractions/
It's not about compile time...
>Our async IO model was based on the Linux industry standard (then and now) epoll, but that is not at all what drove the switch to a polling based model
Can you provide a link to a design document or at the very least to a discussion with motivation for this switch outside of the desire to be as compatible as possible with the "Linux industry standard"?
>the polling based model presents no issues whatsoever with io-uring
There are no issues with io-uring compatibility to such extent that you wrote about a whole blog post about those issues: https://boats.gitlab.io/blog/post/io-uring/
IIUC the best solutions right now are either to copy data around (bye-bye zero-cost) or to use another Pin-like awkward hack with executor-based buffer management, instead of using simple and familiar buffers which are part of a future state.
I'm also curious about this. Boats wrote some about rust async and io-uring a while ago that's interesting[1], but also points out a very clear path forward that's not actually outside the framework of rust's Future or async implementation: using interfaces that treat the kernel as the owner of the buffers being read into/out of, and that seems in line with my expectations of what should work for this.
But I haven't touched IOCP in nearly 20 years and haven't gotten into io-uring yet, so maybe I'm missing something.
Really the biggest problem might be that switching out backends is currently very difficult in rust, even the 0.x to 1.x jump of tokio is painful. Switching from Async[Reader|Writer] to AsyncBuf[Reader|Writer] might be even harder.
[1] https://boats.gitlab.io/blog/post/io-uring/
"Explicit" in language design is catch-all term for several different things: https://boats.gitlab.io/blog/post/2017-12-27-things-explicit-is-not/ (I highly recommend this article.) Defer is still manual and local, just less noisy and burdensome.
Just like every C feature: if you know how it's implemented (and optimized), you will know what is going to happen. All big C compilers already support stack frames and unwinding, so it's not even entirely novel functionality.
While you may object it's not entirely obvious how unwinding is going to be implemented, OTOH in functions with complex control flow it can be easier to understand which `defer`s are going to be run, as opposed to following nested `else` statements, or reasoning about the program state from all `goto cleanup` locations.