let foo = 1;
let foo = "foo";
bar(foo);
というのは、実際には
{
let foo = 1;
{
let foo = "foo";
{
bar(foo);
}
}
}
のようなことをやっているわけで、 shadow された変数が消えるわけでもないし破棄されるわけでもないし上書きされるわけでもない、あくまで shadow されて「名前で」参照できなくなるだけ
同一スコープ内での同名変数の再宣言とシャドウイング|Rustでは再宣言が可能 https://qiita.com/aimof/items/01911a187d7351c8313c
"別の言語ではどうなるか? その2:再宣言できる言語"
"Haskellでは、再宣言可能です。ちなみにHaskellの変数(と呼ぶのが適切かはわかりませんが)はimmutable(不変)です。"
めっちゃくちゃ変化してるじゃん?><;
ただ、この方式だと lifetime あたりの問題があって、たとえば
let name = read_line(); // 所有権ありの文字列
let name = name.trim(); // 旧 name の一部を borrow しているスライス
みたいなことができない (旧 name の生存期間が新 name の生存期間以上でなければならない) ので、万能ではない
たとえば
let age = read_line();
let age = age.parse::<u32>().expect("Invalid age");
みたいにすれば、あるべき型の age にしかアクセスできなくなるので、 age_str と age_int が共存するよりも望ましいと考えることができる
あと、シャドーイングをすると、たとえばこの例だと「2回目の束縛以降で、不正でありうる文字列型の URL データにアクセスできなくなる」という利点がある (URL でなく文字列として取り出したければ、明示的にそういう操作を行うべき)
たとえば
let url = "https://example.com/"; // ここで文字列
let url = Url::parse(url).expect("Invalid URL"); // ここで Url 型
みたいなシャドーイングしたい場合があって、これもまあ賛否分かれるかもしれないけど、あくまで変数の意味を扱ってロジックを書きたいならこれもアリだと思いますね