Code Fix

中級

ブロック内でのシャドーイングは外側の変数に反映されない

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変数への=での再代入)とは別物です。

実務での勘所

Rustがシャドーイングとmutによる再代入という2つの仕組みを両方持っているのは、それぞれ役割が違うためです。mutによる再代入は同じ型のまま値だけを更新しますが、シャドーイングは型そのものを変えることさえできます。let input = "42"; let input: i32 = input.parse().unwrap();のように、文字列から数値へ変換する一連の処理を、同じ変数名を使い回しながら書けるのがシャドーイングの利点です。ただしこの利便性は同じスコープの中でだけ有効で、ブロックをまたぐと別々の変数として扱われてしまいます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)