// 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