And why? I speak when there are not control flow blocks. No if, while, etc.. Obviously, when they exist, we have no choice but to use the second approach. Edit: To clarify, I know it's possible even with control flow. But let's talk about the simple case.
I can see advantages to both styles. When this allows me to use compound operators, I strictly prefer the second. That is, I write:
let mut length = 1;
length += 1;
And not:
let length = 1;
let length = length + 1;
Because of the noise.
But when there are not compound operators, I have doubts what to choose. I can prefer the second because its conciseness, or the first because it reduces the risk for an unwanted mutation. After all, functional languages have only the first for this reason.
Edit: Also, I can see an advantage to the second version because it's easier to understand what variable we refer to in the declaration, esp. for programmers without much background in functional languages.
For non-trivial types there's a semantic difference: new definition doesn't do anything to the old value. It's not changing the old variable, it just creates a new one that happens to have the same name.
Both styles are readable so it really doesn't make much of a difference. I don't think there's a single hard and fast rule, but there are factors that can lead to me choosing once over the other.
For mut:
Compound operators are nice.
The variable is some expensive to clone type like a vec, mutating it is cheaper than allocating a new one.
For shadowing:
I only need to mutate the variable once, having an immutable type might be preferable.
The variable is some cheap to clone/copy type like an integer. If it's cheap to copy, it really doesn't matter if you mutate or shadow.
let a = 1.0;
let a = if rng.gen::<bool>() {
2.0
} else {
3.0
};
You can do this with loops too by using break <some value>.
Create an "a" for me, as some floating point type, and initialize it with 1.0.
Create another "a" for me and initialize it to the value of that previous "a". Oh and by the way, throw that other "a" away.
All of which seems kind of daft. Likely the compiler will optimize that so the same storage is used for both "a"'s, but that is beside the point.
Where I do appreciate such shadowing is where I have some value, which deserves a meaningful name, but I want to change the type I am using to represent it:
let speed = read_speed(); // May well be a float or whatever number type.
...
let speed = s.to_string();
Here, I have the same value with the same name, only the type has changed for convenience. This at least saves having to think up different names for the same thing, "speed_f32", "speed_str" or whatever.
I'm not sure that not "having to think up different names" is worth the problems shadowing can cause. I had a bug that took forever to find along the lines of
fn foo(bar: Bar) {
// A whole bunch of code that among other things defines qux
let bar = qux;
// A whole bunch of code
// Use the new bar when I thought I was using the function argument
Shadowing has it's uses, but I wish my IDE would let me know when I did it.
But to take it a little more seriously, I find shadowing one of the most useful features of Rust. Like I said, programmers that used to imperative languages can find it weird, but I also have a functional background, and for me Rust took the best of both worlds. Knowing to work with this feature is hard, I agree, but completely worth it.
I don't disagree that shadowing is useful. I'm just pointing out that it can be a source of bugs. A subtle indication in my IDE that I've redefined a variable would allow the benefit while giving me a fighting chance to find the kind of bug I managed to create.
Of course you're right, and it's important for newcomers here to note that. There's also difference in the compiler perspective, although performance-wise the both are same (I guess the former takes a little more time to optimize). But after we said that, there's still a place for this discussion, IMHO.
It's somewhat off-topic here, but I would like to say that like the joke in the beginning, it heavily depends on the names you use. IMO, good naming will make shadowing not threatening anymore.
Perhaps because when I do it the scope over which the shadowing occurs is only a handful of lines of code, so any such problem shows up pretty quickly.
But note: If you do something like that, like so:
let mut length = 1;
while some_condition {
....
let length = length + whatever;
...
}
Then the "length" inside the loop is not the same "length" as outside the loop.
After the loop you have the original "length" and it's value "1" again.
I guess this is where such shadowing could cause confusion and bugs.
@ZiCog You're right, and not just that, even in the next iteration the variable will reset. This is not the correct way to rewrite the loop in functional style. Ah, loops are forbidden in functional programming
Edit: See Rust Playground. I used for loop and not while because if I would rely on the length variable to end the loop it was never terminating
I sometimes use both together with this kind of pattern:
let mut x:ComplicatedValue = Default::default();
/* Build `x` the way I want it */
let x = x;
/* Now `x` is immutable, so I know it won’t be accidentally changed. */
Although I think the difference is subtle and no real reason to strictly prefer either exist, I see that just like I though, there are programmers that prefer the mutable approach. So, to help future programmers... You know
Just in case anyone is wondering, I use long, descriptive names. A typical function name is forward_to_out_channel and a variable name recv_port_no. I've had complaints from coworkers who prefer cryptic abbreviations, but, given my age, it's the only way I can remember what I did last week. In spite of that, I managed to inadvertently shadow a name.