Progrust Library.

// dictionary

可変性.

値を書き換えてよいかどうかを表す性質を可変性(mutability)と呼びます。Rustでは変数も参照の指し先も既定では不変(immutable)で、書き換えを許したいときだけmutキーワードを明示的に付けます。

可変性は原則としてデータそのものに備わる性質ではなく、そのデータへアクセスする経路(名前と値を結び付ける変数の束縛や、参照)に付きます。そのため同じ値でも、どの経路から触るかで書き換えの可否が変わります。

fn main() {
    let balance = 1000; // 既定は不変
    // balance += 500; // エラー: E0384(不変の変数への再代入)

    let mut total = balance; // mut を付けたときだけ書き換えられる
    total += 500;
    println!("元の残高: {balance}円 / 加算後: {total}円");
}
Playgroundで開く

変数の可変性と参照の可変性

mutは書く位置によって対象が変わります。let mutのmutは束縛に付き「その変数を書き換えてよい」ことを表します(再代入だけでなく、&mutを作っての書き換えも含みます)。&mutのmutは参照に付き「その参照を通して指し先を書き換えてよい」ことを表します。2つは独立しているため、組み合わせは4通りあります。

書き方 rの差し替え *rでの書き換え
let r = &x; ✗ ✗
let mut r = &x; ✓ ✗
let r = &mut x; ✗ ✓
let mut r = &mut x; ✓ ✓

参照先を書き換えるには*による参照外しを通します。

ムーブで可変性は付け替えられる

可変性が束縛の側に付くことは、値を別の変数へムーブすると分かります。不変な変数が持っていた値でも、mut付きの変数へ移してしまえば書き換えられます。

fn main() {
    let greeting = String::from("こんにちは"); // 不変の束縛
    // greeting.push_str("、世界"); // エラー: E0596(不変の変数は排他借用できない)

    let mut message = greeting; // ムーブ先を mut にすれば書き換えられる
    message.push_str("、世界");
    println!("{message}");
}
Playgroundで開く

逆にmutなしの変数へムーブすれば、そこから先は書き換えられなくなります。

補足

束縛も参照先も可変な場合の動き

let mut r = &mut x;は束縛と参照先の両方が可変なので、指し先の書き換えと参照そのものの差し替えを両方できます。

fn main() {
    let mut price = 300;
    let mut tax = 30;

    let mut r = &mut price; // 束縛も参照先も可変
    *r += 100; // 参照先の値を書き換える
    r = &mut tax; // 参照そのものを別の値へ差し替える
    *r += 10;

    println!("本体: {price}円 / 税: {tax}円");
}
Playgroundで開く
変数以外を借りるときの例外

「借用元もmutで宣言されている必要がある」のは、名前の付いた変数を直接借りる場合の話です。一時値や&mut参照越しの借り直しなど、変数以外の場所を借りるときはこの限りではありません。

fn deposit(balance: &mut i32) {
    let r = &mut *balance; // rはmutでないが、参照越しに借り直せる
    *r += 500;
}

fn main() {
    let mut balance = 1000;
    deposit(&mut balance);
    println!("残高: {balance}円");
}
Playgroundで開く
可変性の検査はコンパイル時だけ

可変性はコンパイラが静的に検査するだけの情報で、値のメモリ表現には現れません。そのためムーブによる可変性の付け替えに実行時のコストはかかりません。

mutはletキーワード専用ではない

mutはletの一部ではなく、値を受け取る側の識別子パターンに付く要素です。そのためパターンを書ける場所であれば、関数の引数・for式のループ変数・match式のアームなどにも同じように付けられます。

fn add_tax(mut price: u32) -> u32 {
    price += price / 10; // 引数を可変な束縛として受け取る
    price
}

fn main() {
    println!("税込: {}円", add_tax(300));
}
Playgroundで開く

引数に付けたmutは関数の内部だけの都合であり、呼び出し側の書き方には影響しません。

constとstaticの可変性

定数(const)は常に不変で、mutを付けることはできません。一方、プログラム全体で1つの実体を持つ静的変数だけはstatic mutと書けますが、データ競合を防げないため読み書きにはunsafeが必要です。さらにRust 2024エディションではstatic mutへの参照を作ること自体がエラー(static_mut_refs)になるため、実質的に使うべきではありません。

共有参照越しに書き換えられる例外

「共有参照&Tの指し先は書き換えられない」というのが原則ですが、標準ライブラリのUnsafeCell<T>を土台とする内部可変性パターンのデータ型はこの制約から外れます。Cell・RefCell・Mutexなどが該当し、&Tしか持っていない状態でも中身を変更できます。

この辞書が使われているページ

backlinks 3

  1. 辞書構造体
  2. 辞書自動参照・自動参照外し
  3. 辞書静的変数