Rustでは、データ構造はデフォルトで不変です。これは、mutキーワードなしに宣言された変数や参照は、初期化後に変更できないことを意味します。
let mut value = 10; // 可変変数 value += 5; let value2 = 10; // 不変変数 // value2 += 5; // コンパイルエラー:cannot assign twice to immutable variable
同じ論理は構造体のフィールドにも適用されます。
struct Point { x: i32, y: i32 } let mut pt = Point { x: 1, y: 2 }; pt.x = 5; // OK、ptがmutとして宣言されているため
ただし、可変性は変数の「最上位」のみに影響します。不変の参照で構造体が格納されている場合、そのデータは、構造体内でmutとして宣言されていても変更できません。
制約を回避する別の方法は、RefCell<T>や原子コンテナなどの特別な型を使用することです。これにより、外見上不変のコンテナ内のデータを「内部可変性(interior mutability)」を使用して変更できます。たとえば:
use std::cell::RefCell; struct Counter { count: RefCell<i32>, } let counter = Counter { count: RefCell::new(0) }; *counter.count.borrow_mut() += 1; // 安全、mutがないにもかかわらず
質問: 構造体のフィールドが明示的にmutとして宣言されているが、変数自体がmutとして宣言されていない場合、そのフィールドの値を変更できますか?
一般的に間違った答え: はい、フィールドがmutとして宣言されていれば、変更できます。
正しい答え: mutキーワードは構造体のフィールドの宣言には使用できません。可変性は、変数自体または取得した参照にのみ関係します。構造体のフィールドを変更するには、変数をmutとして宣言するか、「内部可変性」型(たとえば、RefCell)を使用する必要があります。
例:
struct Foo { val: i32 } // フィールドはmutなしで宣言される let mut foo = Foo { val: 1 }; foo.val = 2; // OK let foo2 = Foo { val: 3 }; foo2.val = 4; // エラー!変数はmutableではない
逸話
大規模なプロジェクトで、ある開発者がフィールドを変更しようとしましたが、フィールドをmut(「struct S { mut x: i32 }」)として宣言すれば必要な可変性が与えられると考えました。これによりコンパイルエラーが発生し、リファクタリングプロセスが数時間延びました。彼は可変性が変数の所有権の特性であって、構造体ではないことを説明されました。
逸話
マルチスレッドに関連するプロジェクトで、データアクセス制御の全ロジックはグローバルな静的構造に保持されていました。「内部可変性」を
RefCellを使用する方法を知らないチームは、Arc<Mutex<T>>やRwLock<T>による単純な解決策の代わりに、冗長に複雑なロックや競合のメカニズムを実装しました。
逸話
開発者は関数シグネチャ内でmut引数を宣言するのを忘れ、その結果、関数が渡された構造体を変更できず、バグは本番環境でのみ発生しました(関数呼び出し後にデータが更新されませんでした)。デフォルトで不変の参照が渡されることを知っていれば、初期段階でエラーが発生しなかったでしょう。