シャドーイング
識別子を定義する際に、既存の識別子と同じ名前を使い、それを覆い隠すような挙動
だいたいのCライクな言語では外側のスコープで定義されたものはシャドーイングできる
シャドーイングする定義にシャドーイングされる識別子を利用できないこともある (JSなど。そっちのが多数派かも)
code:js
const a = 3;
{
const a = 10;
console.log(a); // 10
}
console.log(a); // 3
OCamlなどでは当然のようにシャドーイングできる シャドーイングする定義にシャドーイングされる識別子を使ってもOK
code:ml
let a = 12 in
let a = a + 3 in
print_int a (* 15 *)
これは let a = 12 in (let a = a + 3 in (print_int a)) という見方が正しい
letでシャドーイング定義にもとの識別子を使うと無限再帰になる
遅延評価だからかOCamlと違ってlet rec がないので……
code:hs
let a = 12
let a = a + 3 -- ウオオ無限再帰
Rustでは同一スコープの変数もシャドーイングできる code:rs
let a = 3;
println!("{a}"); // 3
let a = 10;
println!("{a}"); // 10
シャドーイングを使い変数のスコープを調整することでライフタイムの問題を解決できることがある
code:rs
fn move_out<T>(_: T) {}
// 少々恣意的だけど……
fn main() {
let s = "hello".to_string();
let r = &s;
let r = r.repeat(2); // ここで&sを参照していたrは死ぬので
move_out(s); // ここでsを消しても問題 (ダングリングポインタ) にならない
println!("{r}");
}