Rustの変数
Essential前提知識
Rustが変数宣言を**バインディング(binding)**と呼ぶのには理由がある。let x = 5 は名前 x を値 5 に結びつける。バインディングはデフォルトでイミュータブル(immutable)だ——x を読み取ることはできるが、変更はできない。
let キーワード
変数の宣言はすべて let から始まる。
let x = 5;
let greeting = String::from("hello");
型は通常、推論される。推論できない場合、または明示したい場合は、名前の後に型注釈(type annotation)を追加する。
let x: i32 = 5;
let bytes: Vec<u8> = Vec::new();
デフォルトのイミュータビリティ
Rustの変数はデフォルトでイミュータブルだ。次のコードはコンパイルエラーになる。
let x = 5;
x = 6; // error: cannot assign twice to immutable variable
コンパイラがイミュータビリティ(immutability)を強制するのは、読み手が値が変わらないと信頼できるようにするためだ。mut なしで let x = 5 を見たなら、そのスコープ内で x は常に 5 だとわかる——関数の残りをスキャンして確認する必要がない。
再代入を許可するには mut を加える。
let mut x = 5;
x = 6; // ok
バインディングが変化する必要があるときは mut を使い、そうでなければ省略する。
シャドウイング
Rustは**シャドウイング(shadowing)**を許可する。同じ名前で2つ目の let を書くと、最初のバインディングを隠す新しいバインディングが作られる。
let x = 5;
let x = x + 1; // 新しいバインディング、古い x を隠す
let x = x * 2; // さらに新しいバインディング
println!("{}", x); // 12
シャドウイングはミューテーション(mutation)とは異なる。let x のたびに新しいバインディングが作られ、古いものは単に到達不能になる。重要な点として、新しいバインディングは別の型を持てる。
let guess = "42"; // &str
let guess: u32 = guess.parse().expect("NaN"); // u32、同じ名前
mut では型を変えられない——ミューテーションは型を保持しなければならない。シャドウイングは、パイプラインを通じて値を変換し、中間のバインディングが後で不要な場合に慣用的だ。
スコープ
バインディングはその宣言から、それを含むブロックの終わりまで生きる。実行がブロックを抜けると、値は**ドロップ(dropped)**される——メモリが解放されるか、所有されたリソースが解放される。
{
let s = String::from("hello");
println!("{}", s);
} // s はここでドロップされる。ヒープ割り当てが解放される
これがRustのRAIIの実践だ。スコープがバインディングのライフタイム(lifetime)を決定し、コンパイラが値を正しいタイミングでクリーンアップすることを保証する。
型推論
Rustはコンテキストから型を推論する。
let x = 5; // i32(整数リテラルのデフォルト)
let y = 5.0; // f64(浮動小数点リテラルのデフォルト)
let mut v = Vec::new();
v.push(1u8); // v は Vec<u8> と推論される(push から)
コンテキストがない整数リテラルはデフォルトで i32、浮動小数点リテラルは f64 になる。推論の根拠がない場合、コンパイラは注釈を要求する。
let v: Vec<u8> = Vec::new(); // 推論のための push がないため注釈が必要
リテラルのデフォルトを上書きするには型サフィックス(5u8、3.14f32)を付ける。