The reason to be explicit about things (or perhaps a better way to put it is having redundancy), is that it allows the compiler to check multiple things that have to align. In a sense, this is like “checks and balances”. For example, it’s impossible to communicate intent with only a single action. People usually communicate intent by verballing stating intent along with an action that likely aligns with the statement.
That said, if everything in Rust (or life) required an explicit statement of intent with every action, things would be unmanageable. Requiring explicitness is beneficial when the cost of doing so is low and the benefit of verifying the intent is high. Function signatures is a perfect example of this (at least for functions which are not trying to do lots of metaprogramming).
An interesting side effect of requiring multiple matching data points in a language is that when they don’t match, error messages end up teaching people about the requirements of the language.
One place that I think rust gets the tradeoff right is with lifetime elision. This is mostly because the compiler can verify that the code inside the function actually obeys the assumed intent. Therefore, the common cases are handled while complex situations end up resulting in errors when the behavior of the code implementing the function don’t match it’s assumed lifetimes. Note, the interesting thing here is that the reason I think it works well is that the compiler can rely on the function body to know when the user’s intent and the assumed intent based on the elision rules mismatch. Without that, I don’t think it would work as well.
One place I think rust gets the tradeoff wrong is with derefs and all of the various rules, which I still don’t understand, in part, because the compiler always does it and therefore, I can’t learn via getting it wrong. I remember Niko mentioning this as a ‘success’ because before, he just typed *s between the & and the expression. Unfortunately, the part that is missing now is that users never have to learn when derefs are required and therefore, adding *s never transitions from a learning experience to a chore, the learning part just never happens. Perhaps if I understood the rules, I’d think that autoderef was awesome, but unfortunately, I don’t know how I’d learn the rules without Niko (or some other ‘old-timer’) writing a blog post or reading the compiler source code.
In conclusion, just because you have to type seemingly redundant information doesn’t mean that doing so is useless. And just because errors are annoying, doesn’t mean that they don’t provide value to users.