Rustの変数

Essential
最終更新: タグ: Rust

前提知識

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 がないため注釈が必要

リテラルのデフォルトを上書きするには型サフィックス(5u83.14f32)を付ける。