gary17the
> The rust RFC process is a graveyard of good ideas.

I actually have quite an opposite view: I think the Rust core team is 100% correct to make it very hard to add new "features" to the PL, in order to prevent the "language surface" from being bloated, inconsistent and unpredictable.

I've seen this happen before: I started out as a Swift fan, even though I have been working with Objective-C++ for years, considered it an awesome powerhouse and I did not really need a new PL for anything in particular in the world of iOS development. With time, Swift's insistence on introducing tons of new language "features" such as multiple, redundant function names, e.g., "isMultiple(of:)", multiple rules for parsing curly braces at al. to make the SwiftUI declarative paradigm possible, multiple rules for reference and value types and mutability thereof, multiple shorthand notations such as argument names inside closures, etc. - all that made me just dump Swift altogether. I would have to focus on Swift development exclusively just to keep up, which I was not willing to do.

Good ideas are "dime a dozen". Please keep Rust as lean as possible.

dist1ll
I think the dependency situation is pretty rough, and very few folks want to admit it. An example I recently stumbled upon: the cargo-watch[0] crate.

At its core its a pretty simple app. I watches for file changes, and re-runs the compiler. The implementation is less than 1000 lines of code. But what happens if I vendor the dependencies? It turns out, the deps add up to almost 4 million lines of Rust code, spread across 8000+ files. For a simple file-watcher.

[0] https://crates.io/crates/cargo-watch

bjackman
Rust isn't an Exciting New Language any more. It's in the "work towards widespread adoption" phase. Slower feature development is natural and healthy, the stakes are high, mistaken design choices are much more harmful than low velocity at this point.

I'm not excited about Rust because of cool features, I'm excited because it's a whole new CLASS of language (memory safe, no GC, production ready). Actually getting it into the places that matter is way more interesting to me than making it a better language. That's easier to achieve if people are comfortable that the project is being steered with a degree of caution.

knighthack
Since Rustaceans are so neurotic about rewriting everything in Rust, I genuinely thought that an article about rewriting Rust (in Rust) had to be a meta-satirical joke.
dathinab
It's kinda strange how he complains first about a slow decision making process and then lists features which are not stabilized for reasons fully unrelated to the decision making.

E.g. corutines are stuck because they have some quite hard to correctly resolve corner cases, i.e. in the compiler there isn't a full implementation you could "just turn on" but a incomplete implementation which works okay for many cases but you really can't turn on on stable. (At least this was the case last time I checked.) Similar function traits have been explicitly decided to not be stabilized like that for various technical reasons but also due to them changing if you involve future features. (Like async corotines.) Sure the part about return values not being associated types is mostly for backward compatibility but it's also in nearly all situations just a small ergonomics drawback.

And sure there are some backward compatibility related designs which people have loved to do differently if they had more time and resources at the point the decision was made. But also most of them are related to the very early rust times when the team still was much smaller and there where less resources for evaluating important decisions.

And sure having a break which changes a bunch of older decisions now that different choices can be made and people are more experienced would be nice. BUT after how catastrophic bad python2->python3 went and similar experiences in other languages many people agree that having some rough corners is probably better and making a rust 2.0. (And many of this things can't be done through rust editions!)

In general if you follow the rust weekly newletter you can see that decisions for RFC acceptance, including for stabilization are handled every week.

And sure sometimes (quite too often) things take too long, but people/coordination/limited-time problems are often harder to solve then technical problem.

And sure some old features are stuck (corotines) and some but also many "feature gates" aren't "implemented stuck features" (but e.g. things which aren't meant to be ever stabilized, abandoned features, some features have multiple different feature gates etc.)

mplanchard
Shouldn’t read this without also reading Josh Triplett’s comment in response on reddit. One of the core examples in this post is just plain wrong (mutexes), for example: https://old.reddit.com/r/rust/comments/1fpomvp/rewriting_rus...

Edit: nevermind, comment is here too: https://news.ycombinator.com/item?id=41655268

gyre007
One of the things that hit me when I was picking up Rust was that I felt like it had every imaginable feature one could think of - I dont know if Rust team said no to anything (yes I know they obviously must’ve done) - and yet people wanted more and more (some justifiably, others less so) as the language “felt” incomplete or that the features thatd be used by 2% of devs are totally necessary in the language that is “understood” by 1% of developer populace. I’m not saying the author is wrong here, just pointing out how a complex language somehow needs to be even more complicated. Spoiler: it doesn’t. Zig is simpler, arguably faster, with much less drama in the community. I wish more funding went to Zig.
iTokio
Rust mission was already a difficult mix between performance, safety and expressiveness, then the project lost its “founder mode” when Mozilla disengaged, and the original core team mostly left, so no wonder progress slowed down. I personally think that’s it’s better than going down the wrong path.
JoshTriplett
> Now, there are issue threads like this, in which 25 smart, well meaning people spent 2 years and over 200 comments trying to figure out how to improve Mutex. And as far as I can tell, in the end they more or less gave up.

The author of the linked comment did extensive analysis on the synchronization primitives in various languages, then rewrote Rust's synchronization primitives like Mutex and RwLock on every major OS to use the underlying operating system primitives directly (like futex on Linux), making them faster and smaller and all-around better, and in the process, literally wrote a book on parallel programming in Rust (which is useful for non-Rust parallel programming as well): https://www.oreilly.com/library/view/rust-atomics-and/978109...

> Features like Coroutines. This RFC is 7 years old now.

We haven't been idling around for 7 years (either on that feature or in general). We've added asynchronous functions (which whole ecosystems and frameworks have arisen around), traits that can include asynchronous functions (which required extensive work), and many other features that are both useful in their own right and needed to get to more complex things like generators. Some of these features are also critical for being able to standardize things like `AsyncWrite` and `AsyncRead`. And we now have an implementation of generators available in nightly.

(There's some debate about whether we want the complexity of fully general coroutines, or if we want to stop at generators.)

Some features have progressed slower than others; for instance, we still have a lot of discussion ongoing for how to design the AsyncIterator trait (sometimes also referred to as Stream). There have absolutely been features that stalled out. But there's a lot of active work going on.

I always find it amusing to see, simultaneously, people complaining that the language isn't moving fast enough and other people complaining that the language is moving too fast.

> Function traits (effects)

We had a huge design exploration of these quite recently, right before RustConf this year. There's a challenging balance here between usability (fully general effect systems are complicated) and power (not having to write multiple different versions of functions for combinations of async/try/etc). We're enthusiastic about shipping a solution in this area, though. I don't know if we'll end up shipping an extensible effect system, but I think we're very likely to ship a system that allows you to write e.g. one function accepting a closure that works for every combination of async, try, and possibly const.

> Compile-time Capabilities

Sandboxing against malicious crates is an out-of-scope problem. You can't do this at the language level; you need some combination of a verifier and runtime sandbox. WebAssembly components are a much more likely solution here. But there's lots of interest in having capabilities for other reasons, for things like "what allocator should I use" or "what async runtime should I use" or "can I assume the platform is 64-bit" or similar. And we do want sandboxing of things like proc macros, not because of malice but to allow accurate caching that knows everything the proc macro depends on - with a sandbox, you know (for instance) exactly what files the proc macro read, so you can avoid re-running it if those files haven't changed.

> Rust doesn't have syntax to mark a struct field as being in a borrowed state. And we can't express the lifetime of y.

> Lets just extend the borrow checker and fix that!

> I don't know what the ideal syntax would be, but I'm sure we can come up with something.

This has never been a problem of syntax. It's a remarkably hard problem to make the borrow checker able to handle self-referential structures. We've had a couple of iterations of the borrow checker, each of which made it capable of understanding more and more things. At this point, I think the experts in this area have ideas of how to make the borrow checker understand self-referential structures, but it's still going to take a substantial amount of effort.

> This syntax could also be adapted to support partial borrows

We've known how to do partial borrows for quite a while, and we already support partial borrows in closure captures. The main blocker for supporting partial borrows in public APIs has been how to expose that to the type system in a forwards-compatible way that supports maintaining stable semantic versioning:

If you have a struct with private fields, how can you say "this method and that method can borrow from the struct at the same time" without exposing details that might break if you add a new private field?

Right now, leading candidates include some idea of named "borrow groups", so that you can define your own subsets of your struct without exposing what private fields those correspond to, and so that you can change the fields as long as you don't change which combinations of methods can hold borrows at the same time.

> Comptime

We're actively working on this in many different ways. It's not trivial, but there are many things we can and will do better here.

I recently wrote two RFCs in this area, to make macro_rules more powerful so you don't need proc macros as often.

And we're already talking about how to go even further and do more programmatic parsing using something closer to Rust constant evaluation. That's a very hard problem, though, particularly if you want the same flexibility of macro_rules that lets you write a macro and use it in the same crate. (Proc macros, by contrast, require you to write a separate crate, for a variety of reasons.)

> impl<T: Copy> for Range<T>.

This is already in progress. This is tied to a backwards-incompatible change to the range types, so it can only occur over an edition. (It would be possible to do it without that, but having Range implement both Iterator and Copy leads to some easy programming mistakes.)

> Make if-let expressions support logical AND

We have an unstable feature for this already, and we're close to stabilizing it. We need to settle which one or both of two related features we want to ship, but otherwise, this is ready to go.

    > But if I have a pointer, rust insists that I write (*myptr).x or, worse: (*(*myptr).p).y.
We've had multiple syntax proposals to improve this, including a postfix dereference operator and an operator to navigate from "pointer to struct" to "pointer to field of that struct". We don't currently have someone championing one of those proposals, but many of us are fairly enthusiastic about seeing one of them happen.

That said, there's also a danger of spending too much language weirdness budget here to buy more ergonomics, versus having people continue using the less ergonomic but more straightforward raw-pointer syntaxes we currently have. It's an open question whether adding more language surface area here would on balance be a win or a loss.

> Unfortunately, most of these changes would be incompatible with existing rust.

One of the wonderful things about Rust editions is that there's very little we can't change, if we have a sufficiently compelling design that people will want to adopt over an edition.

> The rust "unstable book" lists 700 different unstable features - which presumably are all implemented, but which have yet to be enabled in stable rust.

This is absolutely an issue; one of the big open projects we need to work on is going through all the existing unstable features and removing many that aren't likely to ever reach stabilization (typically either because nobody is working on them anymore or because they've been superseded).

epage
RE: Rust's pacing

I've had a lot of talks with my management about that. For context, I'm on the Cargo team and have authored 11 RFCs (10 approved, 1 pending).

I feel like a lot of the pacing feels slow because:

- As the project matures, polishing whats there takes up a lot of effort

- Conversely, hitting local maximas where things are "just good enough" that individuals and companies don't feel the need to put effort to doing the last leg of work.

- Lack of coordinated teams (formerly Mozilla) doubling down on an idea to hash it out. Hopefully [Project Goals](https://rust-lang.github.io/rfcs/3614-project-goals.html) will help a little in this direction.

- As the project has grown, we've specialized a lot more, making it harder to develop a cross-team feature. It takes finesse to recruit someone from another team to help you finish out a cross-team feature. It also doesn't help we've not done a good job developing the cross-team communication channels to make up for this specialization. Again, Project Goals are trying to improve this. In-person conferences starting back up has also been a big help.

As for RFCs, we've been moving in the direction of choosing the level of process thats appropriate for a decision. Unsure how something will look? You just need approval from 2 members of the relevant team to start a nightly only experiment to flesh out the idea in preparation for an RFC. In Cargo, many decisions don't need wide input and are just team votes on an Issue. RFCs drag out when their isn't a someone from the team shepherding it through the process, the RFC covers too much and needs to be shrunk to better focus the conversation, too much is unknown and instead an experiment is needed, or its cross-team and you need to know how to navigate the process to get the vote done (we want to improve this). As for things being approved but not completed, thats a "we need more help" problem usually.

louismerlin
If I were to rewrite Rust, I'd probably go the route of less features, not more.

Make it 70% of Rust in 10% of the code, similarly to what QBE[0] is doing with LLVM.

You'd probably be able to achieve that if you remove macros and some of the rarely-used features.

[0]: https://c9x.me/compile/

jfyasdfwasdf
Rust 2.0 wishlist:

  * Supports Unions (TypeScript, Flow, Scala3, Hare)

  * Supports GADTs

  * Capable of targeting both preemptive userland concurrency (go, erlang, concurrent Haskell, concurrent OCaml) and cooperative (tinygo, nodejs, async-python, async-rust) without code changes

  * Easily build without libc (CGO_ENABLED=0)

  * No Backwards compatibility promise - This eliminates geriatrics

  * Cleaner syntax, closer to Go, F#, or Python

  * Graph-based Borrow Checker

  * Add `try-finally` or `defer` support, `Drop` is too limiting, Async drop could help.

  * Fix Remaining MIR Move Optimizations and Stack Efficiency

  * Culture for explicit allocator passing like Zig

  * `.unwrap()` is removed
conradludgate
I muuuch prefer pin to any move trait. Pin is a place property, not a type property. I think this post covers it nicely. https://without.boats/blog/pinned-places/. It definitely should be more ergonomic though
eterevsky
I would gladly switch to a Rust fork without async. Even though this article is not about async per se, it’s clear that async makes most of the described problems worse.
hiimkeks
I think the first three items are "add effects, and do it right":

Capabilities to IO can be done by letting IO functions interrupt and call an effect handler, and the caller can specify the effect handler and do access control in there.

The whole Pin situation only exists because async/await was an afterthought and didn't work well with the existing language. async/await is an instance of effects.

I'm excited to start playing with a language that has a good effect system. I am hoping on Ante, but would also like to try Roc at some point.

ephaeton
"Maybe I should fork the compiler and do it myself. Urgh. So many projects. If I could live a million lifetimes, I'd devote one to working on compilers."

-- Maybe you are living a million lifetimes in parallel right now and this one is the one devoted to working on compilers? Get to it! :-)

alkonaut
> most uses of unsafe would also require explicit whitelisting.

I think this is probably where all proposed whitelist/capability proposal discussions end. It's going to be too many crates that are in that category for it to be useful.

A good first step (not sure if it's already taken tbh) would be to at least sandbox build execution. So that an attacker can't execute arbitrary code when your app is compiled.

weinzierl
It's a good collection of the usual suspects, when it comes to suggested improvements for Rust.

The one point that stuck out for me is the comptime section. It approaches the topic from a security and supply-chain attacks angle, which is a way I never thought about it.

rtpg
The function trait section reminded me about effect systems and Purescripts row polymorphism[0], which is a great little way to be able to encode properties of your functions.

I think Rust might quickly run into the “negative trait” problem trying to get that working, while embracing an effect system like Purescripts might get you the goods in a “principled” way. Though I haven’t thought about this deeply.

[0]: https://rtpg.co/2016/07/20/supercharged-types.html

uneekname
I am relatively new to rust (only written a couple thousand lines, haven't fully grokked "idiomatic" rust, etc.) and I feel like I've run into these and similar warts many times. It is weird to lookup, say, coroutines in Rust to learn that everyone seems to agree they should exist, but they won't anytime soon. For a language focused on "correctness" and ergonomics, I think the rust community should consider some backwards-incompatible changes in the name of a better language.
maverwa
I think there are fair complaints and good ideas in this. But I also think thats a bit hypocritical: They complain that there is a gigantic backlog of features in progress (as in "not in stable yet"), and then goes on to propose a lot of additional, quite fundamental and far reaching featues they'd like to see.

Don't get me wrong: I'd like coroutines and a lot of other unstable/hidden features done as well. Function traits sound great, and I'd also like the whole Pin stuff to be easier (or gone?).

But please, "Lets just extend the borrow checker and fix that" sounds very demeaning. Like no one even tried? I am by far no expert, but I am very sure that its not something you "just" go do.

I like most of the proposed features and improvements, I mostly share the critique on the language, but I do not thing the "why not just fix it?" attitude is helpful or warranted. Theres tons of work, and only so much people & time.

culebron21
I think adding per-crate permissions to do undoable/unsafe things will lead us to permissions hell of devops in big deployments. Like Amazon S3 with gazillion options. I think it's time to do something radically different with 3rd party deps.

Even if we put aside safety issues, each crate brings ~10 more dependencies by default (i.e. without any features turned on), which bloats compile times. Maybe it's better to be able to shard 3rd party crates, and not update them automatically at all?

olivierduval
> Most crates I use - like human-size or serde don't need any special capabilities to work. So we don't need to worry so much about their authors "turning evil" and adding malicious code to our software

well... :-(

Actually, it's obvious that some authors might "turn evil" dumbly, by abusing some kind of priviledged permissions. By chance, these kinds of supply-chain risks are "easily" identified because

1) the permissions are an "easy" risk indicator, so you can priorize either to pin the version library (after validating it) or validate the new version

2) not so many libraries will use these permissions so you "have time" to focus on them

3) in these libraries, the permissions will tell you what system call/bad effects is possible, so will allow you to narrow even more the scope of investigation

So, IMHO, permissions are not really the end of all but only a tiny step.

The real problem is "how can human-size be used to subvert the program ?" For example: what is happening if the returned size "forget" or "add" 100 bytes to files bigger than 1 KB ? As a remininder, STUXNET was about some speed a tiny bit faster than planned and shown...

tommiegannert
> Rust doesn't have syntax to mark a struct field as being in a borrowed state.

> ast_nodes: Vec<&'Self::source str>,

Oh, that would be neat to replace the https://github.com/tommie/incrstruct I wrote for two-phase initialization. Unlike Ouroboros and self_cell, it uses traits so the self-references can be recreated after a move. Whether it's a good idea, I don't know, but the magic Ouroboros applies to my struct feels wrong. But I say that as someone coming from C++.

> if let Some(x) = some_var && some_expr { }

Coming from Go, I was surprised that something like

    if let Some(x) = some_var; expr(x) { }
isn't a thing.
OnorioCatenacci
From the examples he's mentioned, it sounds like there's quite a bit of [bike shedding] (https://en.wikipedia.org/wiki/Law_of_triviality) in the process for RFC's in Rust. That's a problem when you've got lots of smart people trying to help.
binary132
When I look at the way C++ has been developed and implemented over the years, I can’t help but think that a relatively small and questionably sustainable group of compiler engineers, no matter how passionate (and people do burn out) cannot possibly hope to sustain development of Rust indefinitely, especially as the project’s complexity and convolutions continue to further complicate and convolve. The only reason C++ has been able to is that it has very extensive industrial sustenance behind at least two of its major compilers, and presumably they also help keep GCC up to par. I don’t know, maybe GCC can somehow set the standard for what Rust can hope to do over the years, but it seems like a minor miracle from the outside.
FrustratedMonky
"the coroutines RFC has lasted longer than World War 1 or 2"

This sounds bad, but I wonder how many features have taken this long to include in other languages. Is this really as out of step as it sounds?

There is the move-fast-break-things mentality, but is that how you want to design a language?

Seems like we are missing some middle ground step, where there are good features, maybe even done, and stable, but they aren't getting worked into the main language.

Maybe a decision making problem.

dietr1ch
Marking fixed stack size (and maybe even with an actual bound) would be helpful to ensure the tail-call optimisation is being done.

I don't think any language helps verifying that., and even in the ones that require it by spec, it's unclear if it's happening. Maybe you didn't really wrote a tail-recursive function because of a helper that you expected to be inlined. I guess it's easy to notice if you try to blow the stack in a unit test though.

mwcampbell
> a fully baked language - warts and all. Python 2.7 for life.

I still wish the Python core team had abandoned the Python 3 experiment and gone with Python 2.x for life, warts and all. I learned to work with the warts, including the Unicode ones. I think a lot of us did.

bli940505
>And I don't know if it will ever be there. Progress on the language has slowed so much. When I first started using it, every release seemed to add new, great features in stable rust. Now? Crickets.

Is frustration with Rust on the rise? I just started using Rust few month ago and absolutely love it. I can't tell what's going on with the Rust foundation so I can only judge by reading sentiments. Nothing would kill my vibe harder than knowing smart people thinks the language isn't doing great :(

johan_felisaz
The section on comp time is written in a way which makes you think that zig invented the concept. It slightly irritated the lisper in me...

Great article apart from that.

smolder
I hate to say it since I'm generally against this kind of obstructive elitism, but I think that maybe one of the good things about rust is it's user-unfriendliness to amateurs. It has massive utility and ergonomy inherent to its design, but gatekeeping to keep away "left-pad" library authors and users is good for utility, too.
gavinhoward
I explain how Rust missed the mark in [1].

[1]: https://gavinhoward.com/2024/05/what-rust-got-wrong-on-forma...

tskulbru
Article aside, that page background messed with eyes while reading
Avi-D-coder
yep,i agree on all of this and suspect a large portion of long timers do too.

Some one just has to do it.

nikolay
If functions are "fn", then coroutines should be "co". In terms of verbosity, Rust turned into Java.
adastra22
Do it! I’d use this language.
sam0x17
These are actually great and I would gladly welcome these changes if they were implemented in stable.

My wishlist:

* allow const fns in traits

* allow the usage of traits in const exprs. This would allow things like using iterators and From impls in const exprs, which right now is a huge limitation.

* allow defining associated type defaults in traits. This can already be worked around using macros somewhat effectively (see my supertrait crate) but real support would be preferable.

* allow eager expanding of proc macro and attribute macro input, perhaps by opting in with something like `#[proc_macro::expand(tokens)]` on the macro definition. Several core "macros" already take advantage of eager expansion, we peasants simply aren't allowed to write that sort of thing. As a side note, eager expansion is already possible for proc and attribute macros designed to work _within proc macro crates_, for example this which I believe is the first time this behavior was seen in the wild: https://github.com/paritytech/substrate/blob/0cbea5805e0f4ed...

* give build.rs full access to the arguments that were passed to cargo for the current build. Right now we can't even tell if it is a `cargo build` or a `cargo doc` or a `cargo test` and this ruins all sorts of opportunities to do useful things with build scripts

* we really need a `[doc-dependencies]` section in `Cargo.toml`

* give proc macros reliable access to the span / module / path of the macro invocation. Right now there are all sorts of projects that hack around this anyway by attempting to locate the invocation in the file system which is a terrible pattern.

* allow creating custom inner attributes. Right now core has plenty of inner attributes like `#![cfg(..)]` etc, and we have the syntax to define these, we simply aren't allowed to use custom attribute macros in that position

* revamp how proc macro crates are defined: remove the `lib.proc-macro = true` restriction, allowing any crate to export proc macros. Facilitate this by adding a `[proc-macro-dependencies]` section to `Cargo.toml` that separately handles proc-macro-specific dependencies. Proc macros themselves would have access to regular `[dependencies]` as well as `[proc-macro-dependencies]`, allowing proc macro crates to optionally export their parsing logic in case other proc macro crates wish to use this logic. This would also unblock allowing the use of the `$crate` keyword within proc macro expansions, solving the age old problem of "how do I make my proc macro reliably refer to a path from my crate when it is used downstream?"

* change macro_rules such that `#[macro_export]` exports the macro as an item at the current path so we can escape from this ridiculousness. Still allow the old "it exports from the root of the current crate" behavior, just deprecate it.

bilekas
Seems rust has come full circle. Rewriting everything in rust.. Why not Rust! /s
raverbashing
You know, I agree

And there's a lot of things that are weird or clunky

I honestly don't "get" the "no classes, just struct methods thing" and while, sure, C++ is kinda like that, but the ergonomics are weird. I'd much rather have the class/methods declaration as most languages do

Lifetimes are good but the implementation is meh. Most cases could do with a default lifetime.

Copy/borrow strictness is good to think about but in most cases we don't care? Copy should probably the default and then you borrow in special cases

LetMeLogin
I stopped reading at "Like the first iPhone - which was amazing by the way."

That phone couldn't even send MMS.... You had to jailbreak it to be able to do normal stuff that the phones could do for ages back then.

Havoc
The drama around rust leadership and also in kernel has me more spooked tbh. It’s more of a threat to the long term viability of the lang. Flaws in a language are to some extent expected and can be worked around.

Languages like C++ and python are wildly successful and don’t think anyone would call them perfect.

The dependence point is valid but not sure that is easily solvable in general. Doesn’t seem like a rust issue. See npm and python pip - blind trust is par for the course except in very rigorous environments

csomar
> You can't tell that something is borrowed until you try to compile your program. (Aside: I wish Rust IDEs made this state visible while programming!)

I am not sure what the OP is using, but with LSP I do get the error message in my editor (nvim) before any compiling (though am pretty sure some checking in happening in the background).

> Compile-time Capabilities

Not sure how this makes any sense when Rust compiles to multiple targets. Should all libraries become aware of all the "capabilities" out there. Also, this already can be implemented using features and keep things minimal.

> Comptime

I can't make sense of what the OP issue is here.

> Make if-let expressions support logical AND. Its so simple, so obvious, and so useful. This should work: if let Some(x) = some_var && some_expr { }

The example makes no sense.