This is not why dyn was introduced. It was introduced to remove ambiguity and make trait implementations easier to read/write. From the RFC:
The current syntax is often ambiguous and confusing
Because it makes traits and trait objects appear indistinguishable. Some specific examples of this:
This author has seen multiple people write impl SomeTrait for AnotherTrait when they wanted impl<T> SomeTrait for T where T: AnotherTrait .
impl MyTrait {} is valid syntax, which can easily be mistaken for adding default impls of methods or adding extension methods or some other useful operation on the trait itself. In reality, it adds inherent methods to the trait object.
Function types and function traits only differ in the capitalization of one letter. This leads to function pointers &fn ... and function trait objects &Fn ... differing only in one letter, making it very easy to mistake one for the other.
In fact, the RFC also points out that introducing dyn as a 'warning' or syntax salt is not sufficient motivation:
The current syntax is often ambiguous and confusing, even to veterans, and favors a feature that is not more frequently used than its alternatives, is sometimes slower, and often cannot be used at all when its alternatives can. By itself, that's not enough to make a breaking change to syntax that's already been stabilized.
Ignoring oddity of that syntax for a second, the bigger issue is that there are two kinds of mutability/immutability in Rust:
& and &mut which are critical for Rustâs concept of safety, and have to be strongly enforced. Here mutability is about the referenced value.
mut in bindings, which is not needed for safety, and itâs more like a lint or a comment for the programmer. Here mutability is about the binding (variable) not the value, so it doesnât apply when accessing the value indirectly.
The second kind is not really needed by the language. It doesnât directly affect safety, correctness or code generation. If it was removed from the language (i.e. let was made same as let mut, and mut became meaningless no-op) nothing would break!
So really all let/let mut is for is a ânote to selfâ for the programmer to state the intention clearer. Itâs a shorter version of // this is the one and only assignment to this variable vs // I'm going to be mutating this variable later.
âŚso any proposal that makes it âeasierâ to change values of let variables just makes this let/let mut distinction even weaker and more useless.
That was my initial interpretation as well, but OP's later comments led me to revise my interpretation. I suspect that if Rust had used a sigil such as § for the mutability attribute, rather than the alphabetic sequence mut, these confusions between verbs (let) and attributes (§, for mut) would never arise.
I strongly disagree. Multiple times Iâve been experiencing warnings like âx doesnât need to be mutableâ - and every time it was signal about huge (logical) mistake in my code. Itâs not just lint, itâs much more. Also, âimmutable by defaultâ is not just lint, this approach can help to create code with different architecture, different basis - it helps to write more safe code.
Eh. When I see stuff like this I canât help but feel that people are misplacing their enjoyment of immutability in general. Here is how I honestly feel about it:
Not being able to get a &mut U from a &T is one of the core principles of Rust.
Not being able to get a &mut U from an immutable T is an annoyance (when writing code) that, in my experience, is almost always correctly solved by adding a mut. But, it has two great payoffs:
First, it makes the unused_mut lint one of the top most useful bug-catching lints, right up there with unused bindings. Oftentimes I might know for instance that I want to collect some data from an iterator into a vector, and then sort it. Thanks to mut being required I now have it trained in me to write let mut vec: Vec<_> = in anticipation of the sort:
but then after all of that, I often forget to finish what I started and actually call sort!
Second, reading the code after it is written, anybody who sees mut can expect that something will happen to the value after its initial assignment. (this is, of course, also thanks to the unused_mut lint)
I can contrive an example where adding mut is not the solution:
fn ancient_function() {
let items = {
things.iter()
.some_long_and(complicated)
.pipeline_of(iterator, adapters)
.collect::<Vec<_>>()
};
+ items.sort(); // sort the output that gets printed
print_report(&items);
save_file(items); // (note: expects items to be in original order)
}
but to make a mistake like that seems⌠rather careless, enough so that I doubt that the author would notice it even after the minor speedbump presented by the "missing mut" error.
I don't understand why it is more of an annoyance than the lack of a &T -> &mut U conversion. Both are erroneous; disallowing this is not any more "annoying" than e.g. disallowing calls to nonexistent functions.
Having a &T means that other code can observe changes to the value.
Having a T means that other code can't. I mean, it's a stronger condition than even having a &mut T!
This point of view is simply colored by the present state of affairs. The compiler has many valid reasons to disallow calls to nonexistent functions (for starters, it can't typecheck them!). Requiring a mut keyword on
a binding in order to mutate it is a completely arbitrary decision.
Being able to get a &mut U from an immutable T would not affect soundness. Being able to get a &mut U from a &T would. That is, &T vs &mut T is required for memory safety; mut on bindings is not.
It might be arbitrary from the perspective of soundness, but it certainly isn't completely arbitrary in the sense of lacking a strong rationale for the behavior.
The requirement encourages better code style by discouraging mutability, indicating the fact that we have learned from the past mistakes of other languages. It's hard to find a style guide for e.g. C++ or JS that doesn't say to use const wherever possible. This alone was more than enough to justify its inclusion.
I guess my feeling is partly that, now that we have been writing in rust for a while, we've had the chance to learn even more; and what I feel that I've learned is that the &/&mut distinction alone may very well have been enough to wipe away all of those classes of mistakes that beg for immutability by default in other languages.
I now see where you are coming from, but I still disagree that we should just trust programmers again, since âweâve been writing Rust for a whileâ. People are still terrible at writing perfect (as in âcorrectâ) code, and throwing away some of the clutches that we already have seems like a clear regression.
To me, let mut provides a good "heads up" marker that I need to go into an imperative mode of thinking; if it isn't there, I stay in a mode in which I know that the probability of mutation is lower (interior, yada...).
I think having let mut also encourages a more functional writing style that bends towards using iterators; in turn, this reduces the distance to parallelization.
All in all I think let mut was and remains the better decision and I think we should still enforce it.