変数と不変性 — Rubyの自由な再代入が消えた理由
let, mut, shadowing, const — Ruby変数と1:1比較
Rubyでx = 10と書けばx = 20にいつでも変えられる。Rustは違う。let x = 10;で宣言するとこの値は不変だ。変えようとするとコンパイルエラーになる。
let x = 10;
// x = 20; // error[E0384]: cannot assign twice to immutable variable
Rubyのfreezeが全変数にデフォルト適用されたと思えばいい。
mut — 明示的な可変宣言
値を変えたければmutを付ける。
let mut x = 10;
x = 20; // OK
「この変数は変わります」とコードを読む人に伝えるもの。Rubyでは全変数が暗黙的にmut。Rustは意図の明示を要求する。
shadowing — 同じ名前で再宣言
Rubyでx = "hello"がx = 42になっても問題ない。型が変わってもいい。Rustでこれに近いのがshadowingだ。
let x = "hello"; // &str
let x = x.len(); // usize — 型が変わってもOK
let x = x + 1; // 前のxを隠して新しいx
mutとの違い:shadowingは新しい変数を作ること。前のxにはもうアクセスできない。mutは同じ変数の値を変えること。
// mutは型を変えられない
let mut x = "hello";
// x = 5; // コンパイルエラー!型が違う
// shadowingは型も変わる
let x = "hello";
let x = 5; // OK — 新しい変数
Ruby開発者にはshadowingの方が自然に感じるかもしれない。実際のRustコードでもよく使う。特に文字列をパースして数値に変換する時、同じ名前を再利用するパターンが一般的だ。
const — RubyのCONSTANT
RubyのMAX_SIZE = 100のように定数を宣言する時はconstを使う。
const MAX_SIZE: usize = 100;
letとの違い:
constは必ず型を明示する必要があるconstはコンパイル時に値が決まる(ランタイム計算不可)constはどこでも使える(関数の外でもOK)
Rubyでは定数に再代入すると警告が出るが実行はされる。Rustではconstの再代入は不可能。
まとめ
| Ruby | Rust | 説明 |
|---|---|---|
x = 10 |
let x = 10; |
不変 |
x = 10; x = 20 |
let mut x = 10; x = 20; |
可変 |
x = "hi"; x = 42 |
let x = "hi"; let x = 42; |
shadowing |
MAX = 100 |
const MAX: usize = 100; |
定数 |
キーポイント
letはデフォルト不変 — Rubyのfreezeがデフォルトのようなもの
mutを付ければ可変 — Rubyでは全変数が暗黙的にmut
shadowingは同じ名前で新変数を作る — 型も変えられる
constはコンパイル時定数 — RubyのCONSTANTより厳格
メリット
- ✓ 不変デフォルトのおかげで意図しない値の変更が根本から遮断される
- ✓ shadowingでRubyのように柔軟な変数再利用が可能
デメリット
- ✗ 習慣的にmutを付けがちだが、可能なら不変にするのがRust慣例
- ✗ shadowingを乱用すると同じ名前の変数が異なる値を持ち混乱