5 ms·
I think the section about immutability should mention that this is possible in Rust: fn main() { let x = true; let x = false; } I was pretty disap
by teamhappy 10y ago
I think the section about immutability should mention that this is possible in Rust:
fn main() {
let x = true;
let x = false;
}
I was pretty disappointed when I found out about it.
- eudox 10y agoThose are two different bindings. That is, 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.
- sixbrx 10y agoVery useful indeed when you want a canonical or corrected (or otherwise "improved") version of a value and want to make sure that the original can't accidentally be used in what follows (which can be a hard to detect bug).
- eudox 10y agoYes, a common refinement pattern in code is (pseudocode): def fn(object): let object = predicate(object) ? modify(object) : error(); // do something with the refined object Or something along those lines.
- teamhappy 10y agoI know a lot of people really like shadowing. I obviously don't. GHC has an option to emit a warning and if Rusts compiler adds one for it as well (maybe it has one by know? I don't know) I guess I'm okay with it.
- steveklabnik 10y agoClippy has a lint, but it's not turned on by default, even in Clippy.
- Manishearth 10y agoWe have three, in fact, which disallow various kinds of shadowing. For example, some people like `let x = <expr containing x>` shadows but not `let x = <totally unrelated>`. But everyone has wildly different style choices here so we leave all three off by default and you can turn them on if you want. I have yet to see a bug in Rust code caused by shadowing, though.
- tormeh 10y agoCan I write let x = 1; let x = x + 1; ?
- steveklabnik 10y agoYes.
- tormeh 10y agoAnd is the behavior exactly the same as it would be if I omitted "let" on the second code line? If the answer is yes, isn't it just mutability with different syntax then? 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.
- solidsnack9000 10y ago> I would guess shadowing wouldn't affect values shared with a different thread... That is actually a big deal, though.
- yokohummer7 10y agoIt doesn't have the same semantics, because even after introducing the new variable, it is still immutable. So you cannot accidentally modify the new variable. e.g., 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.
- steveklabnik 10y agoThe behavior is not the same. It's a new variable, entirely. So you'll have two integers on the stack, one of them is not accessible any more. You can see this difference if you add a scope around the second x, and then print the value of the original x after it goes out of scope. 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...
- pcwalton 10y agoI was responsible for this design decision. For me, shadowing is really useful and I use it all the time. It's especially useful for creating new, slightly different modifications of earlier values when you don't want to keep coming up with new names all the time—this is common when making heavy use of immutable variables. 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.
- teamhappy 10y ago> I was responsible for this design decision. 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.
- bluejekyll 10y agoThank you, seriously. I use shadowing all the time, especially when unwrapping results and options. 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.
- breatheoften 10y agoAre there any languages that use a different syntax for shadowing assignment? As in: 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.
- solidsnack9000 10y agoShadowing and mutability are not the same thing. Shadowing within the same scope is admittedly unusual.