This topic originates from a sibling thread Pre-RFC: flexible `try fn`. If you want to catch up on that but don’t want to read it whole (it is long and sometimes a bit heated), I try to sum it up here (but I’m necessarily biased) as well as expand and propose some actual possible steps (not all proposals are fully mine and they generally come out of discussions either directly or indirectly as inspiration). I also tried to pick up a list of relevant comments at the end of this post.
I believe each topic deserves its own thread, and as my comment started the somewhat off-topic discussion there, I feel moral obligation to try to move the discussion here and stop polluting the original topic.
The problem
Programming languages naturally accumulate bloat over their life time. This is because once a feature gets into the language, it’s really hard or impossible to get it out again.
I haven’t seen anybody arguing that having a lot of bloat in the language is a good thing so it might be worthwhile exploring ways how to try avoiding or at least slowing down this kind of degeneration. At least watching this forum, it I feel there’s a lot of proposals that would add to that bloat without much benefit if they got in. (I won’t mention specifics in an attempt to avoid off-topic flame wars. I also don’t claim that some of them did get in.)
The obvious solution to not introduce any new changes isn’t really a viable way, especially with a language as young as Rust.
Current state
There are two mitigation mechanisms I know of. The first one is the RFC process and Rust’s teams which tries to block the bloat from getting in.
The other one are editions which have limited abilities to remove some features from the language.
Limitations of the current state
It is not possible to remove some features with the editions mechanism. With the others, while not tried yet, I have a feeling removing a feature once in the language would prove much harder than getting it in. So in general, most features are going to stay in once they get there. But even if we took only the features that are not possible to remove, it would still be a limitation.
As for the RFC process, the problem is similar to airport security. First, it needs constant vigilance. This requires energy that could be better used somewhere else. If the team has to pay attention to every RFC that is not going to get in, if only to decide to close it, it still takes time. Second, it needs to „defend“ the language many times, but failing once has dire consequences. No matter how good the defenders are, they are going to let something slip from time to time. Furthermore (unlike the airport security), the current implementation of this defence has somewhat low visibility (compared to high visibility of proposals on this forum, for example) and people with somewhat pessimistic life viewpoint (like me ) worry needlessly.
Proposals
Negative RFCs
Edit: Let’s scratch this one. As pointed out below, it’s heavy-weight and doesn’t really effectively solve the problem of repeated proposals. Something like including them in the FAQ, if they happen often enough, or an easily searched index of refused ideas might be better.
First, I think it would make sense to remove the one-sideness of the process. The process has a formalized way to let a feature in forever. But if a proposal is not accepted, it doesn’t stop anyone from proposing it again only painted in a different colour. Let’s extend it so there’s also a way to ban a feature forever. There probably are features that are never going to be let into Rust, but this is only informal folklore. These crop up from time to time. Having a place where all the arguments against are summed up and can be linked to could both put up ones mind at ease and save time explaining it every time. If a feature is desired, it gets implemented and stabilized. If a feature is explicitly not desired, it gets on a no-feature list. Of course, there would be still the current way of closing a proposal with no implications for similar ones.
It has been hinted that this is already possible in the current process. I’m not familiar with the RFC process enough to know for sure if writing an RFC „Never include feature X“ would really work correctly and there’s no precedent. So maybe only a precedent is what is needed ‒ something obvious, like „We are never going to include C++ style move constructors, move is memcpy on purpose“? This one appears from time to time and it is argued this would break a lot of unsafe code, etc.
Be a bit more explicit about some properties of a good RFC
Edit: Including the points in the template was just my first idea. Basically, some place where a potential author can find the suggestions is good. Maybe creating some kind of guide for new authors (which would include other recommendations too, if the current authors were willing to share them) might make sense.
I guess it implicitly is the authors responsibility to prove why the RFC is needed, useful and worth it (of course in both cases, positive and negative). I’d like to just be more explicit about that, to both remind the authors about it and to make it easier to notice when the proposal isn’t actually needed.
I would also like to promote fact-based discussions over guessing where possible. I believe it is quite hard to predict how users might react to a feature, how it will interact with other features, etc. Having at least some guidance usually helps.
The things I propose here are often supposed to have psychological effect, so the wording might need some care (I know I’m not particularly good at that thing, so if you are or know a psychologist or someone with the expertise, I’ll welcome the help).
In other words, I’d like the authors to include these things in their texts (by suggesting it in the RFC template):
- Problem statement: I think this could replace or enhance the „motivation“ section, as it is a very similar in actual meaning. However, on the subconscious level, this has a bit different connotations. It hints that there should be something broken to fix and not be done „just because“. This could lead the author to think and abandon obviously unneeded RFC even before writing it. Furthermore, having an explicit problem stated (instead of eg. a solution ‒ motivation „Make X more like in language Y“ is a motivation, but not a problem statement) makes it easier for others to come up with a counter-proposal that solves the same problem in a different way, possibly better.
- Why this one is different: In case there was a similar proposal before that was rejected, an additional section explaining how this one addresses the points against the previous one and why the author believes this one is better. This implicitly asks the author to look for similar previous proposals and take their feedback into consideration. It also helps reviewers get faster up to date and highlights the important differences. I believe the series of module path RFCs did work somewhat on this basis and it did have positive effect (I would have to go read them if the differences were explicitly highlighted, but I’m sure they evolved based on feedback).
- Benefit vs. cost analysis: Every feature has certain cost on the bloatness budget (in rare cases the cost may be negative, if a feature removes inconsistency). Some approaches are more expensive than others, so authors actively thinking about that are more likely to pick lightweight approaches. It might promote thinking about some 80/20 solution ‒ one where there is a substantial improvement on the problem situation while requiring much smaller feature. It would also encourage gathering some statistics (how much does this problem happen in the wild?) if reasonably easy to get by. Another suggestion could be to try a more conservative approach first ‒ if the proposal can be split in two, one weaker and another that the first can be extended into, it IMO makes sense to try the smaller one first and follow with the second only if the first solution is still inadequate (eg. start small and upscaling as needed, not start big and downscaling during the RFC process).
- How it fits in the general direction of Rust ‒ probably a part of the reference level explanation? This should discourage „alien looking“ features, or features with expected negative impact on writing correct code.
- Experiments and research: The goal would be to encourage some previous experimentation, like possibly creating something similar or limited as a proc macro ‒ a great example of this is the futures-await crate. Having something similar would both better show-case the intention and provide the author with feedback much earlier than when it gets implemented in the compiler, potentially leading to higher quality design.
These things are „soft“, so there’s no guarantee they actually would help. On the other hand, it would still keep the RFC process flexible ‒ as like now, omitting a section in case it makes no sense wouldn’t need to be penalized in any way.
Next steps
I would really like to hear what others think about this. On one side I think this has some potential to help without too many drawbacks (the only one I can think of is that this would probably increase the effort needed to write an RFC). Unlike language features, the changes to the template could be reverted if they prove to be bad.
On the other side, the fact that I see very few drawbacks is usually an indicator that I’m blind by bias, so feedback is welcome. Is there a chance this could backfire in some way? But as this is a very fresh and raw idea, I believe more productive approach will be to first discuss the general direction and bikeshed details (like what section each point fits in best) later on.
If the general consensus is this is a good idea, I’d like someone to help me with the wording and such things, both because I’m not native English speaker and because I sometime write things people understand differently than I thought.
The promised links to relevant posts in the other thread (I hope I didn’t miss any and that there are not too many off-by-one errors in the post numbers). When not sure if a post is relevant, I tried to err on the side of including it:
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
- Pre-RFC: flexible `try fn`
I just hope this does not escalate to another very heated discussion.