The discussion is getting a bit derailed, but in a way that I think is useful – talking a bit here about our process, and how we think about tradeoffs, is a healthy and worthwhile discussion to have. So I want to respond a bit more with my perspective.
/me gets up on the soapbox…
Respectfully, I disagree with that “the middle ground seems mostly ignored”. The lang team works very hard to acknowledge, understand, and dig into opposing viewpoints. And indeed, a large point of the RFC process is to fully map out the tradeoffs, and make sure they are fully accounted for.
I very much agree that the middle ground is vital! And that’s why, for example, I talked a bit about mitigations for things like stray files.
The module system thinking has gone through, no exaggeration, probably a dozen iterations at this point, largely driven through feedback and concerns that a diverse range of people have raised. I’m working on writing up another variant today.
In all the cases you cite, the design shifted as a result of concerns about code reasoning. (E.g. the match design had the initial RFC closed, and a new one with a completely different, more locally-drive design was ultimately merged.)
Let me step back for a second and make some general points:
-
It’s very rare for a design to have no downsides. Design is full of tradeoffs. With Rust, we tend to strive for designs that “bend the curve”, eliminating traditional tradeoffs (e.g. concurrency without data races), but that doesn’t mean we can avoid downsides altogether.
-
As such, it’s possible to simultaneously acknowledge that something is a downside of a design, but still be in favor of the design. For example, I fully acknowledge that a downside of reading module structure from the file system is that stray file cause problems, which in turn invalidates some existing workflows. But (1) I think we can mitigate those issues (as I explained in the post) and (2) I personally don’t see that particular downside as a deal-breaker.
-
The RFC process is about exploring the tradeoff space and the design space. This requires open-minded iteration and constraint gathering. As people voice concerns and objections, these are often incorporated as new constraints on the design. This iteration process often requires going through strawman designs that would be poor places to end at, but that introduce key new ideas. The design process around modules, for me, has given a ton of insight into the problems we might try to solve. I had no expectation that the strawman design in this post would ever land as-is, but was hopeful that it could spur useful discussion and further iteration.
-
One of the things that makes language design especially difficult is that you are designing a platform for a huge range of people with different needs, perspectives, and styles. Good design requires letting go of your personal perspective, and trying to understand, empathize with, and internalize a wide range of alternative perspectives, then reach a balanced decision. One of the things we look for in people joining the subteams is precisely this ability to “get into other people’s heads”, to hold a wide variety of use cases in mind simultaneously. This often involves seeking out stakeholders who have very strong opinions and needs (for example, in the ongoing rand redesign, making sure to include crypto specialists and try to balance their needs with others). It can be hard to see this work happening, but it is! I think that if you take an honest look at technical discussions, you will see people genuinely engaging with arguments on all sides.
-
We all have to acknowledge that sometimes there will be decisions that we disagree with; this is a fact of life. What matters is that these decisions were the result of a legitimate deliberation process that explored the tradeoffs. The subteams work very hard to ensure this is the case, and I think we’ve gotten a lot better at it over time, though we’re by no means perfect. At the end of the day, though, someone has to provide a value judgment on the tradeoffs, and that is the key role of Rust’s leadership. (See this podcast for more).
-
Finally, with regard to explicit vs implicit: I think that these terms are nearly toxic for debate. Design is far more nuanced than such a binary labeling, and the right decisions here are ultimately context-dependent. We need to focus on concrete experiences when reading and writing code to fully grasp the tradeoffs. It’s especially unhelpful to talk in terms of general trends here; the details matter! I put a lot of work into spelling out a nuanced way of thinking about these tradeofs, in terms of three dimensions:
-
Applicability. Where are you allowed to elide implied information? Is there any heads-up that this might be happening?
-
Power. What influence does the elided information have? Can it radically change program behavior or its types?
-
Context-dependence. How much of do you have to know about the rest of the code to know what is being implied, i.e. how elided details will be filled in? Is there always a clear place to look?
So in short: we all want Rust to be better, even if we disagree about what better means. The best way to make progress is to be introspective and empathetic, to try to understand deeply why both we and others feel the way they do. And then to try to take on other’s values as potential design constraints alongside our own, and collaboratively iterate toward more balanced designs. I think some amount of this kind of brainstorming has happened on this thread, but we could all be doing a better job of making sure to say “I see where you’re coming from” – and meaning it.