fn main() {
let x = true;
let x = false;
}
I was pretty disappointed when I found out about it. fn main() {
let x = true;
let x = false;
}
I was pretty disappointed when I found out about it.Some people don't like it, and if you don't you can use clippy to disallow it. From what I've seen, most Rust code does use shadowing.
Dun dun duuun
I used to write a lot of C++ and my idea of immutability always was that if I see a variable declaration at the top of a function and scroll down to where it's used (without reading the code in the middle) I can assume nothing happened to the variable.
Then I started to learn Haskell something like a year ago and found out that shadowing is not only allowed but used by people. Especially in Haskell (or OCaml for that matter) where you can just use x' for a slightly modified version of x. I don't know. Maybe I get used to it one day.
I don't know if it's idiomatic, but I find it keeps things cleaner by not having to define lots of different names for essentially the same variable.
Scope 1
let x = 1
let x = 2
=> error, rebinding variable in scope not allowed
Scope 2 let x = 1
shadow x = 2
=> works!
Scope 3 shadow x = 2
=> error: no variable x to shadow declared in scope
I wonder how well something like that would work in practice. fn main() {
let x = true;
{
let x = false;
}
}
Since you don't use let for assignment, it's a non-problem. And it can be useful when you're doing some computation where you want to keep intermediate variables (for debugging or clarity), and rather than think of the proper name for each, you just successively bind the same name. def fn(object):
let object = predicate(object) ? modify(object) : error();
// do something with the refined object
Or something along those lines. Can I write
let x = 1;
let x = x + 1;
?I would guess shadowing wouldn't affect values shared with a different thread, because it's technically a new value, but in single-thread use it would be equivalent to assignment.
That is actually a big deal, though.
let x = 1;
let x = x + 1;
x = 99; // Error: you can't accidentally modify the immutable variable `x`
let mut x = 1;
x = x + 1;
x = 99; // Ok
You can even turn back a mutable varaible into an immutable variable, like this: let mut x = 1;
x = x + 1;
let x = x;
x = 99; // Error: `x` became immutable
This can be useful to "pin" the variable after a large number of calculations done to it.Consider this program:
fn main() {
let x = 5;
let r1 = &x;
let x = x + 1;
let r2 = &x;
println!("r1: {:p}", r1);
println!("r2: {:p}", r2);
}
This will print something like r1: 0x7fff955e3dfc
r2: 0x7fff955e3dec
Note that while you can't access the original x, it's still there, and references to it still work. Because the value of x was copied, not mutated.Also, given compiler details like SSA, I'd argue that in a certain sense, mutation is similar to this, rather than this being similar to mutation...
> isn't it just mutability with different syntax then?
For one thing, in order to take a mutable reference to a binding, that binding must be declared as `mut`. let mut a = 2;
let b = 3;
let x = &mut a;
// let y = &mut b; // error: cannot borrow immutable local variable `b` as mutableShadowing within the same scope is admittedly unusual.