ブロック内でのシャドーイングは外側の変数に反映されない
if文などのブロックの中でletを使って同じ名前の変数を再宣言(シャドーイング)すると、それはブロック内だけで有効な別の変数になります。ブロックを抜けると、外側の変数は変更されないまま残ります。
なぜエラーが出ないのか
出力: inside: 120
(エラーなし)- Rustは何も報告しません。型としても借用としても正しいコードだからです
出力: inside: 120- 実際の出力 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 fn main() { 2 let mut total = 100; 3 let bonus = 20; 4 if bonus > 0 { 5 let total = total + bonus; ^ 6 println!("inside: {}", total); 7 } 8 println!("outside: {}", total); 9 }
出力: inside: 120
outside: 100
letによる再宣言はブロック内だけで有効な新しい変数を作ります。外側のtotalを更新したいなら代入(=)を使います。
直し方: let total = を total = にします。
広告
広告スロット(未設定)
パターン2
1 fn main() { 2 let mut balance = 500; 3 let deposit = 100; 4 if deposit > 0 { 5 let balance = balance + deposit; ^ 6 println!("inside: {}", balance); 7 } 8 println!("outside: {}", balance); 9 }
出力: inside: 600
outside: 500
letで作った変数はブロックを抜けると消え、外側の変数には一切影響しません。
直し方: let balance = を balance = にします。
パターン3
1 fn main() { 2 let mut level = 1; 3 let gained = 2; 4 if gained > 0 { 5 let level = level + gained; ^ 6 println!("inside: {}", level); 7 } 8 println!("outside: {}", level); 9 }
出力: inside: 3
outside: 1
シャドーイングと再代入は見た目が似ていますが、変数のスコープに与える影響が全く違います。
直し方: let level = を level = にします。
パターン4
1 fn main() { 2 let mut stock = 30; 3 let arrived = 10; 4 if arrived > 0 { 5 let stock = stock + arrived; ^ 6 println!("inside: {}", stock); 7 } 8 println!("outside: {}", stock); 9 }
出力: inside: 40
outside: 30
ブロックの外にも反映させたい更新は、シャドーイングではなくmut変数への代入で行う必要があります。
直し方: let stock = を stock = にします。
パターン5
1 fn main() { 2 let mut hp = 50; 3 let heal = 15; 4 if heal > 0 { 5 let hp = hp + heal; ^ 6 println!("inside: {}", hp); 7 } 8 println!("outside: {}", hp); 9 }
出力: inside: 65
outside: 50
letを付けた瞬間に、それはまったく別の変数として扱われ、外側の変数とは無関係になります。
直し方: let hp = を hp = にします。
よくある誤解
「同じ名前でletし直せば、外側の変数もその値に更新されるはず」という思い込みは誤りです。letによるシャドーイングは常に新しい変数を作る操作であり、既存の変数への代入(mut変数への=での再代入)とは別物です。
まとめ
ブロック内でのシャドーイングは外側の変数に反映されないは中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。