(no title)
jackosdev | 2 years ago
It’s just so damn useful and I’m not sure what the downside is, it sucks when you have to keep coming up with different names so you can keep around an identifier that you don’t need anymore.
jackosdev | 2 years ago
It’s just so damn useful and I’m not sure what the downside is, it sucks when you have to keep coming up with different names so you can keep around an identifier that you don’t need anymore.
tialaramex|2 years ago
Rust's Into::into() is consuming the object in the old (now shadowed) rect variable. So conveniently the old rect variable which we can't access also no longer has a value†. In many languages a method can't consume the object like that, so the old object still exists but we can't access it because it is shadowed.
For example in C++ they have move semantics, but their move isn't destructive, so the object is typically hollowed out, but still exists until the end of the scope at least.
Rust's type strictness matters here too. It means if you later modify some code using rect meaning whatever it was before that statement morphing it into a Rect<f32> chances are it doesn't type check and is rejected. For example in many languages if (rect) { ... } would be legal code and might change meaning as a result of the transformation, but in Rust only booleans are true or false.
† Unless this previous variable's type implemented the Copy trait and therefore it has Copy semantics and consuming it doesn't do anything.
MrJohz|2 years ago
Fwiw, I find this feature very useful, and it's helpful more often than it is a nuisance. But there are no guarantees that you're consuming or transforming the object you're shadowing, and the compiler won't necessarily help you out if you simply accidentally use the same name twice.
mr_00ff00|2 years ago
I assume accessing it is undefined behavior?
I would assume you could change this without affecting backwards compatibility.
masklinn|2 years ago
Also useful because you can’t have abstracted local types, so let’s say you’re building an iterator in a language with interfaces you could do something like
But in Rust that won’t work, every adapter yields a different concrete type, you’d have to box every layer to make them compatible. Intra-scope shadowing solves that issue.The biggest downside is that it’s possible to reuse names for completely unrelated purposes, which can make code much harder to understand. Clippy has a shadow_unrelated lint but it’s allowed by default because it’s a bit limited.
eptcyka|2 years ago
alpaca128|2 years ago
The downside is that you may get a weird bug and only after a while see that you accidentally overwrote a function parameter and the Rust compiler didn't even warn you about it.
For this reason I always add the following line to my projects to enable warnings:
You can also use "deny" instead of "warn" to make it an error. I also like "#![deny(unreachable_patterns)]", which detects bugs in enum pattern matching if you accidentally match "Foo" instead of "Type::Foo" - I honestly don't know why this isn't set by default.masklinn|2 years ago
If you “overwrite” a function parameter without using it, the compiler will warn you of an unused variable.
If you “overwrite” a function parameter because you’re converting it, it’s a major use case of the feature.
> I honestly don't know why this isn't set by default.
Because the author of the match can’t necessarily have that info e.g. if you match on `Result<A, B>` but `B` is an uninhabited type (e.g. Infallible), should the code fail to compile? That would make 95% of the Result API not work in those cases. Any enum manipulating generic types could face that issue.
IIRC it was originally a hard error, and was downgraded because there were several edge cases where compilation failed either on valid code, or on code which was not fixable (for reasons like the above).
the_mitsuhiko|2 years ago
It will absolutely warn about this:
results instouset|2 years ago
To "accidentally" overwrite it, you have to either:
sophiabits|2 years ago
My day job is predominantly in Typescript and a lot of code winds up reading significantly worse than it needs to. A common pattern for me is unique-ifying some sort of array—“const dataUnique = new Set(data);” is horrible, and if there’s no reason to keep the original “data” variable in scope then it’s doubly bad; I want to keep as little context in my head as possible.
cillian64|2 years ago
That said, I still think sparing use of this is justified, especially with an editor which can show types on mouseover.
jackosdev|2 years ago
iudqnolq|2 years ago
signaru|2 years ago
FpUser|2 years ago
To me either has pros and cons.
ithkuil|2 years ago
Shadowing is not a big deal with IDEs; you can always see the type of the variable , jump to definition easily etc etc.
The rule to not shadow variables makes more sense when you want to understand the code just by looking at it.
layer8|2 years ago
I’ve tripped over unexpected shadowing often enough that I wish more languages would forbid it. I rarely have trouble choosing appropriate variable names to avoid shadowing.
pcwalton|2 years ago
andrepd|2 years ago
tialaramex|2 years ago
alpaca128|2 years ago