let x = 5;
let x = x + 1;
這不是單純的「x 改值」,而是:
第二個 let x = ... 是重新宣告一個「新的變數」,遮蔽掉前一個 x。
也就是說:
第一個 x 活在前一個 scope 中
第二個 x 則是新的變數,雖然名字相同,但其實是 全新的 x
📌 它被稱為 「遮蔽(shadowing)」,就是因為:
舊的變數還在作用域中,但它的名字已經被新的覆蓋(像被陰影遮住一樣)
let spaces = " ";
let spaces = spaces.len(); // 將字串變成整數
🔸 Python 的寫法:
x = 5
x = x + 1
➡️ 是同一個變數 x 的值被改變(mutable by default)
🔸 JavaScript 的情況(視關鍵字而定)
用 let 或 const 時:
let x = 5;
let x = x + 1; // ❌ 會報錯:Identifier 'x' has already been declared
JavaScript 不允許在同一個 block 裡用 let 宣告同名變數(不像 Rust)。
用 var 時:
var x = 5;
var x = x + 1; // 合法,但其實只是被覆蓋了(非 block-scope)
但 var 是 function-scope,不是 block-scope,風險大且不建議用。
Rust 支援 shadowing 的目的有幾個:
let x = "123";
let x = x.parse::<i32>().unwrap(); // 原本是 &str,後來變成 i32
語言 | 是否允許同名變數重新宣告 | 是否有「變數遮蔽」語意 | 行為描述 |
---|---|---|---|
Rust | ✅ 可以,會產生新變數 | ✅ 有 | 重新定義一個變數,舊變數仍存在但被遮蔽 |
Python | ✅ 可以,但只是改值 | ❌ 沒有 | 修改原本的變數值,並非新變數 |
JavaScript | ❌ let 不行,var 可 |
❌ 沒有 | 重新賦值,非 shadowing;使用 let 會報錯 |
let mut count = 1;
count = count + 1;
println!("{}", count); // 2
這裡是 同一個變數 count,被賦予新值。
let count = 1;
let count = count + 1;
println!("{}", count); // 2
這裡是 重新宣告一個新的 count,前一個 count 被遮蔽掉(shadowed)
let spaces = " ";
let spaces = spaces.len(); // ✅ OK:由 &str → usize(shadowing)
let mut spaces = " ";
spaces = spaces.len(); // ❌ 錯誤:mut 無法改變型別
比較項目 | shadowing (變數遮蔽) |
mut (可變變數) |
---|---|---|
宣告方式 | let x = 5; let x = x + 1; |
let mut x = 5; x = x + 1; |
是否建立新變數 | ✅ 是(重新宣告) | ❌ 否(改變原變數) |
是否可改變型別 | ✅ 可以(例:&str → usize ) |
❌ 不可以(型別需一致) |
是否可變性切換 | ✅ 可從 immutable ➝ mutable | ❌ 不可變更 mut 狀態 |
適用情境 | 型別轉換、階段性處理、語意更清楚 | 值需改變但型別不變的情境 |
常見風格 | 函數式風格 | 命令式風格(imperative) |
mut
就能改變值與型別,更偏向安全的重新定義。